题目链接


题目大意:

一开始给你只有一个点111的树,有qqq次询问。每次询问有两种操作

  • 1pv1\;p\;v1pv 就是把最小的没加入的点,加入这个树,它的父亲是ppp,权值是vvv
  • 2u2\;u2u 就是询问你u的Strength(Su)Strength(S_{u})Strength(Su​)是多少?

SuS_uSu​的直接定义是一个集合这个集合包括这个点里面所有的直接儿子的Strength(Sson)Strength(S_{son})Strength(Sson​),和当前点的权值组成的集合

Strength(S)=∣S∣⋅∑d∈SdStrength(S)=|S|\cdot\sum_{d\in S}dStrength(S)=∣S∣⋅d∈S∑​d


解题思路:

  1. 首先我们知道对于每个点的值uuu是这么算的就是它所有子树里面的点,一直往上跳直到点uuu,每次跳到一个点我们就乘以(跳到点的直接儿子个数+1),那么就是这个点对uuu节点的答案贡献。那么所有的子树的点贡献加起来就是uuu的答案了

  2. 但是这么搞很多条路径直接到不同的点很难维护,我们可以假设全部询问都是根节点

  3. 我们可以算出每一个节点对根节点的贡献因子mi。
    设d(i)d(i)d(i)是节点iii的儿子节点数+1.
    设m(i)m(i)m(i)就是从iii到根节点的简单路径上的d(i)d(i)d(i)的乘积。

  4. 当新加入一个节点uuu到节点ppp时:我们观察可以发现,仅仅改变了ppp和ppp的子树的m(i)m(i)m(i).

  5. 显然是m(i)=m(i)∗(d(p)+1)/d(p).m(u)=m(p)m(i)=m(i)*(d(p)+1)/d(p). m(u)=m(p)m(i)=m(i)∗(d(p)+1)/d(p).m(u)=m(p)。

  6. 我们需要一个数据结构可以 对一个区间进行乘,更新一个值,以及求一个区间的总和。

  7. 我们用线段树维护dfn序。
    那么一个节点的子树就是一段连续的区间。
    我们求一个节点的贡献时。显然就是sumsumsum同时除以他们的公共路径的m(i)m(i)m(i)。
    由于乘法是一个浮点数。我们先累乘所有的分子,再累乘所有的分母,最后我们用乘法逆元
    可以求出取模。

  8. 时间复杂度O(q×(logn+log(mod)))O(q\times(logn+log(mod)))O(q×(logn+log(mod)))


AC code

#include <bits/stdc++.h>
#define mid ((l + r) >> 1)
#define Lson rt << 1, l , mid
#define Rson rt << 1|1, mid + 1, r
#define ms(a,al) memset(a,al,sizeof(a))
#define log2(a) log(a)/log(2)
#define lowbit(x) ((-x) & x)
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define INF 0x3f3f3f3f
#define LLF 0x3f3f3f3f3f3f3f3f
#define f first
#define s second
#define endl '\n'
using namespace std;
const int N = 2e6 + 10, mod = 1e9 + 7;
const int maxn = 500010;
const long double eps = 1e-5;
const int EPS = 500 * 500;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> PII;
typedef pair<ll,ll> PLL;
typedef pair<double,double> PDD;
template<typename T> void read(T &x) {x = 0;char ch = getchar();ll f = 1;while(!isdigit(ch)){if(ch == '-')f*=-1;ch=getchar();}while(isdigit(ch)){x = x*10+ch-48;ch=getchar();}x*=f;
}
template<typename T, typename... Args> void read(T &first, Args& ... args) {read(first);read(args...);
}int n, m, now = 1;
int d[maxn];
vector<int> G[maxn];
struct Q {int op, u, v;
};
vector<Q> e;
int L[maxn], R[maxn], tot, fa[maxn];
void dfs(int u) {// 求每个子树的区间[L[u],R[u]]L[u] = ++ tot;for(auto it : G[u]) dfs(it);R[u] = tot;
}inline ll qim(ll a,ll b) {//快速幂ll res = 1;while(b) {if(b&1) res = res * a % mod;b >>= 1;a = a * a % mod;}return res;
}struct Segtree {ll tr[maxn << 2];// 维护区间加ll tag[maxn << 2];// 维护乘法void pushdown(int rt) {int mul = tag[rt];tag[rt<<1] = (tag[rt<<1]*mul) % mod;tag[rt<<1|1] = (tag[rt<<1|1]*mul) % mod;tr[rt<<1] = (tr[rt<<1]*mul) % mod;tr[rt<<1|1] = (tr[rt<<1|1]*mul) % mod;tag[rt] = 1;}void pushup(int rt) {tr[rt] = (tr[rt<<1]+tr[rt<<1|1]) % mod;}void build(int rt, int l, int r) {tag[rt] = 1;tr[rt] = 0;if(l == r) return;build(Lson);build(Rson);}void segmul(int rt, int l, int r, int L, int R, int val) {if(L <= l && R >= r) {tag[rt] = (tag[rt] * val) % mod;tr[rt] = (tr[rt] * val) % mod;return;}pushdown(rt);if(L <= mid) segmul(Lson,L,R,val);if(R > mid) segmul(Rson,L,R,val);pushup(rt);}void update(int rt, int l, int r, int pos, int val) {if(l == r) {tr[rt] = (val * tag[rt]) % mod;// 这里新加入一个点相当于把它激活了以前是0,不考虑贡献,要让它乘上路径上的d(i), 也就是tag[rt]return;}pushdown(rt);if(pos <= mid) update(Lson,pos,val);else update(Rson,pos,val);pushup(rt);}ll ask(int rt, int l, int r, int L, int R) {if(L <= l && R >= r) return tr[rt];// 询问区间加pushdown(rt);ll res = 0;if(L <= mid) res = (res + ask(Lson,L,R)) % mod;if(R > mid) res = (res + ask(Rson,L,R)) % mod;return res;}ll quary(int rt, int l, int r, int pos) {if(l == r) return tag[rt];// 询问路径上面的d(i)的乘积pushdown(rt);if(pos <= mid) return quary(Lson,pos);else return quary(Rson,pos);}
}sgt;int main() {IOS;read(n,m);for(int i = 1; i <= m; ++ i) {int op, p, v;read(op,p);if(op==1) {read(v);now ++;G[p].push_back(now);e.push_back({op,p,v});} else {e.push_back({op,p,-1});}}dfs(1);sgt.build(1,1,tot);now = 1;d[1] = 1;sgt.update(1,1,tot,1,n);for(int i = 0; i < m; ++ i) {int op = e[i].op;if(op == 1) {int p = e[i].u, v = e[i].v;d[++now] = 1;fa[now] = p; int sub = qim(d[p],mod-2);d[p] ++;sgt.segmul(1,1,tot,L[p],R[p],d[p]);// mu[p]*(d[p]+1)/d[p]sgt.segmul(1,1,tot,L[p],R[p],sub);sgt.update(1,1,tot,L[now],v);// 把单前点在线段树里面激活}  else {int v = e[i].u;ll tmp=1;if(v!=1) tmp=qim(sgt.quary(1,1,tot,L[fa[v]]),mod-2);// 除以路径上面的乘积cout << sgt.ask(1,1,tot,L[v],R[v]) * tmp % mod << "\n";}}
}

线段树 ---- D. Power Tree(离线dfs序+线段树维护树上多条路径和的技巧)相关推荐

  1. New Year Tree(dfs序+线段树+二进制)

    题意: 给出一棵 n个节点的树,根节点为 1.每个节点上有一种颜色 ci.m次操作.操作有两种: 1 u c:将以 u为根的子树上的所有节点的颜色改为c. 2 u:询问以 u为根的子树上的所有节点的颜 ...

  2. Codeforces 343D Water Tree(DFS序 + 线段树)

    题目大概说给一棵树,进行以下3个操作:把某结点为根的子树中各个结点值设为1.把某结点以及其各个祖先值设为0.询问某结点的值. 对于第一个操作就是经典的DFS序+线段树了.而对于第二个操作,考虑再维护一 ...

  3. codeforces E. Jamie and Tree LCA+dfs序+线段树

    题解: 写起来还稍微有点麻烦. dfs序+线段树可以维护子树的整体修改和查询. 因此,这道题我们要往子树上靠. 我们首先从1号点进行dfs遍历,顺便求出点的dfs序和深度,然后我们采用倍增的思想,可以 ...

  4. 牛客小白月赛12 H 华华和月月种树 (离线dfs序+线段树)

    链接:https://ac.nowcoder.com/acm/contest/392/H 来源:牛客网 时间限制:C/C++ 2秒,其他语言4秒 空间限制:C/C++ 131072K,其他语言2621 ...

  5. 【POJ - 3321】 Apple Tree(dfs序 + 线段树维护 或 dfs序 + 树状数组维护)

    题干: There is an apple tree outside of kaka's house. Every autumn, a lot of apples will grow in the t ...

  6. 求和(dfs序+线段树)

    题意: 已知有n个节点,有n−1条边,形成一个树的结构. 给定一个根节点k,每个节点都有一个权值,节点i的权值为vi​. 给m个操作,操作有两种类型: 1 a x :表示将节点a的权值加上x 2 a ...

  7. 瓜瓜的时空旅行,第三次模拟赛,dfs序+线段树维护最小值

    题目描述 西瓜们生活在编号 1⋯n 的 n个平行时空中,2n−2 台时光机将这些平行时空联系在一起.一台时光机有 3个整数参数 u,v,t 表示从时空 u 可以花费 t 的时间穿梭到时空 v.为了确保 ...

  8. [CodeForces877 E. Danil and a Part-time Job]dfs序+线段树

    [CodeForces877 E.Danil and a Part-time Job]dfs序+线段树 分类:Data Structure SegMent Tree dfn 1. 题目链接 [Code ...

  9. bzoj3252攻略 贪心+dfs序+线段树

    题目链接:戳这里 3252: 攻略 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 605  Solved: 255 [Submit][Status] ...

最新文章

  1. ipvsadm使用简介
  2. httpwatchv11.1.46.0免费版
  3. 上传问题总结(文件大小检测,大文件上传)
  4. 安卓清理垃圾清理代码_用方面清理代码
  5. [LeetCode]Basic Calculator
  6. linux 日志面试题,Linux运维 | 面试题
  7. 猛料一顿狂堆!华为P40 Pro详细参数被曝光
  8. index mysql_mysql 原理~ index的详解
  9. linux vim 粘贴 没有保持原来的格式,linux中的剪贴板用法,实现vim中原格式粘贴...
  10. 今天吃什么随机网页_初秋应该多吃什么水果?饮食禁忌有哪些?今天燕多多跟你一起探究...
  11. python--修改证件照的大小
  12. Java 编程练习之:101-200之间的素数
  13. 11月13日科技资讯|天猫回应“双11数据造假”:已启动司法流程;小米折叠手机专利曝光;ASP.NET感染勒索软件|极客头条
  14. python检测按键按下_如何检测按键是否被按下?
  15. python re.sub和lambda_【python学习笔记】 re.sub()
  16. 我看男人的眼光,是不行
  17. linux中|管道符的作用
  18. JS对异步循环使用递归,分批进行大量异步请求
  19. ThingsBoard MQTT链接、掉线报警、数据转换规则引擎
  20. Wonderful Life

热门文章

  1. Redis中集合set数据类型(增加(添加元素)、获取(获取所有元素)、删除(删除指定元素))
  2. 如何理解机器学习中的嵌入 (Embeddings)?
  3. SpringBoot与SpringMVC的区别是什么?
  4. JSP第二次作业_3小题
  5. 【OpenCV 4开发详解】图像与视频的保存
  6. C++ Primer英文版(第5版)
  7. Hexo 个人博客 SEO 优化(3):改造你的博客,提升搜索引擎排名
  8. CIA败给维基解密 专家称AI将改变黑客行为
  9. AI-2048 注释
  10. 阿里云的一些奇怪问题及服务器配置