题目链接


题目大意:


问题转化:

很容易发现:假设修改的节点是vvv.
1.vvv的子树sonvson_vsonv​直接加上(n−size[sonv])n×d\frac{(n-size[son_v])}{n}\times dn(n−size[sonv​])​×d
2.不是vvv的子树直接加上size[v]n\frac{size[v]}{n}nsize[v]​
3.vvv点加上nn∗d\frac{n}{n}*dnn​∗d
我们可以把除nnn提出来只维护上面的乘法

解法1:根号分治

  1. 我一开始想的是dfs序用差分进行单点修改区间求和,毕竟是整颗子树的加,但是有个问题是它可能是菊花图,你每次都给子树进行区间修改的时候复杂度会变成O(nlogn)O(nlogn)O(nlogn)非常大,但是查询是logloglog的。
  2. 我们想一想可不可以吧复杂度均摊了?
  3. 上面算法超时的原因是因为可能存在菊花图?子树大小是O(n)O(n)O(n)个,我们考虑一下均摊这个O(n)O(n)O(n),就是我们取sqrt(n)sqrt(n)sqrt(n),对于小过sqrt(n)sqrt(n)sqrt(n)的我们直接修改,对于大于sqrt(n)sqrt(n)sqrt(n)我们打个标记给它,把它转到查询的时候才考虑
  4. 查询的时候我们只要去看那些大过sqrt(n)sqrt(n)sqrt(n)的点,因为这些点的贡献还没算?我们可以再查询的时候暴力统计就好了,因为大过sqrt(n)sqrt(n)sqrt(n)只要不超过sqrt(n)sqrt(n)sqrt(n)个,然后,统计的时候我们要判断这些点和询问点的父子关系,用于判断加的权值是哪一种?这个要用LCA\text{LCA}LCA去判断。那么查询的复杂度是
  5. O(q∗sqrt(n)∗logn)→查询[1,dfn[v]]的值,枚举大过sqrt的点,判断父子关系统计贡献O(q*sqrt(n)*logn)\rightarrow查询[1,dfn[v]]的值,枚举大过sqrt的点,判断父子关系统计贡献O(q∗sqrt(n)∗logn)→查询[1,dfn[v]]的值,枚举大过sqrt的点,判断父子关系统计贡献
  6. 这个复杂度常数优化是可以过的!!–(快读,手动取模,能int就int)
#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 = 998244353;
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, q, lim;
vector<int> G[maxn];
int L[maxn], R[maxn], father[maxn][20], depth[maxn], siz[maxn], tot;
int tag[maxn];
//.............
void MOD(int &a, int b) {a = a + b;if(a >= mod) a -= mod;if(a < 0) a += mod;
}
int tr[maxn];
inline void add(int pos, ll val) {val %= mod;while(pos < maxn) {MOD(tr[pos],val);pos += lowbit(pos);}
}inline int sum(int pos) {int res = 0;while(pos) {MOD(res,tr[pos]);pos -= lowbit(pos);}return res % mod;
}
//...........
inline ll qim(ll a, ll b) {ll res = 1;while(b) {if(b & 1) res = (res * a) % mod;a = a * a % mod;b >>= 1;}return res;
}void dfs(int u, int fa) {depth[u] = depth[fa] + 1;father[u][0] = fa;for(register int i = 1; i <= 18; ++ i)father[u][i] = father[father[u][i-1]][i-1];L[u] = ++ tot;siz[u] = 1;for(auto it : G[u]) {if(it == fa) continue;dfs(it,u);siz[u] += siz[it];}R[u] = tot;
}inline int LCA(int u, int v) {int delta = depth[u] - depth[v];int backup = u;for(register int i = 18; i >= 0; -- i)if(delta >> i & 1) u = father[u][i];if(u != v) return -1;for(register int i = 18; i >= 0; -- i) // 找直接的儿子if((delta-1)>>i&1)backup = father[backup][i];return backup;
}vector<int> h;int main() {// IOS;read(n,q);int lim = sqrt(n);int inv = qim(n,mod-2);for(register int i = 1; i < n; ++ i) {int u, v;read(u,v);G[u].push_back(v);G[v].push_back(u);}dfs(1,0);for(register int i = 1; i <= n; ++ i) if(G[i].size() > lim)h.push_back(i);while(q--) { int op, v, d;read(op,v);if(op == 1) {read(d);if(G[v].size() > lim) MOD(tag[v],d);else {// 给 L[v] 这个点加 n*dadd(L[v],1ll*n*d);add(L[v]+1,-1ll*n*d);for(auto it : G[v]) {if(it == father[v][0]) continue;add(L[it],1ll*(n-siz[it])*d);add(R[it]+1,-1ll*(n-siz[it])*d);}// 给[L[v],R[v]]之外的区间加上siz[v]*dadd(1,1ll*siz[v]*d);add(n+1,-1ll*siz[v]*d);add(L[v],-1ll*siz[v]*d);add(R[v]+1,1ll*siz[v]*d);}} else {int ans = sum(L[v]);for(auto it : h) {if(it == v) {//第三种int tmp = (int)(1ll*tag[v]*n%mod);MOD(ans,tmp);continue;}int is = -1;if(depth[it] < depth[v]) is = LCA(v,it); int tmp;if(~is) tmp = (1ll*tag[it]*(n-siz[is])%mod);// 第一章else tmp = (1ll*tag[it]*siz[it]%mod);// 第二种MOD(ans,tmp);}printf("%d\n",(1ll*ans*inv%mod+mod)%mod);}}return 0;
}
/*
5 3
1 2
1 3
2 4
2 5
1 1 1
1 2 2
2 3
*/

解法2:树链剖分

  1. 上面有个很大的问题就是子树太多怎么办?
  2. 我们看到整个子树都是加上d⋅(n−size[sonu])/nd\cdot(n-size[son_u])/nd⋅(n−size[sonu​])/n
  3. 只和那个点关?那么我们可以把树变成若干条重链!,就是对于vvv的子树,我们只修改重儿子,然后我们在点vvv打个标记就好了!!,那怎么查询呢?
  4. 如果询问是重儿子里面的点肯定是没问题的,如果是询问轻儿子的时候,我们沿着重链跳,在跨链的时候肯定会访问到vvv点,跳到这个点vvv的时候再累加贡献!!,然后其他的和上面一样对外子树就是vvv子树外的点还是直接差分加,vvv子树里面的点只修改重儿子的值
  5. 你询问的时候沿着重链一直跳就刻意了,把祖先上面未统计的点−>->−>top[x]top[x]top[x]和fa[top[x]]fa[top[x]]fa[top[x]]之间的贡献统计上面上来


绿色部分就是要修改的部分!!


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 = 998244353;
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, q, lim;
vector<int> G[maxn];
int dfn[maxn], top[maxn], father[maxn], siz[maxn], tot, son[maxn];
ll tag[maxn];
//.............
void MOD(ll &a, ll b) {a = a + b;if(a >= mod) a -= mod;if(a < 0) a += mod;
}
ll tr[maxn];
inline void add(int pos, ll val) {val %= mod;while(pos < maxn) {MOD(tr[pos],val);pos += lowbit(pos);}
}inline ll sum(int pos) {ll res = 0;while(pos) {MOD(res,tr[pos]);pos -= lowbit(pos);}return res % mod;
}
//...........
inline ll qim(ll a, ll b) {ll res = 1;while(b) {if(b & 1) res = (res * a) % mod;a = a * a % mod;b >>= 1;}return res;
}void dfs(int u, int fa) {father[u] = fa;siz[u] = 1;int max_son = -1;for(auto it : G[u]) {if(it == fa) continue;dfs(it,u);siz[u] += siz[it];if(max_son < siz[it]) {son[u] = it;max_son = siz[it];}}
}void dfs1(int u, int t) {dfn[u] = ++ tot;top[u] = t;if(!son[u]) return;dfs1(son[u],t);for(auto it : G[u]) {if(it == father[u] || it == son[u]) continue;dfs1(it,it);}
}int main() {read(n,q);int lim = sqrt(n);int inv = qim(n,mod-2);for(register int i = 1; i < n; ++ i) {int u, v;read(u,v);G[u].push_back(v);G[v].push_back(u);}dfs(1,0);dfs1(1,1);while(q --) {int op, v, d;read(op,v);if(op == 1) {read(d);add(1,1ll*siz[v]*d);add(n+1,-1ll*siz[v]*d);add(dfn[v],-1ll*siz[v]*d);add(dfn[v]+siz[v],1ll*siz[v]*d);MOD(tag[v],d);if(son[v]) {// 修改重儿子的值v = son[v];add(dfn[v],1ll*(n-siz[v])*d);add(dfn[v]+siz[v],-1ll*(n-siz[v])*d);}} else {ll ans = sum(dfn[v]);MOD(ans,1ll*tag[v]*n%mod);for(v=top[v];v;v=top[father[v]]) {// 每次都是直接跳到链顶MOD(ans,1ll*(n-siz[v])*tag[father[v]]%mod);}printf("%lld\n",(ans*inv%mod+mod)%mod);}}return 0;
}
/*
5 2
1 2
1 3
2 4
2 5
1 1 1
2 5
*/

树链剖分 or 根号分治 + dfs序 + 树状数组 ---- CF1254 D. Tree Queries相关推荐

  1. LG P3345 [ZJOI2015]幻想乡战略游戏(树的带权重心 + 树链剖分+ 动态点分治)

    题目 首先这个题看起来这个最小值不好在动态点分治时维护. (居然)可以转化. 最小值于树的带权重心处取到. 带权带的是点权. 树的带权重心是子树权∗2>=总树权子树权*2>=总树权子树权∗ ...

  2. 蒟蒻浅谈树链剖分之一——两个dfs操作

    树链剖分,顾名思义就是将树形的结构剖分成链,我们以此便于在链上操作 首先我们需要明白在树链剖分中的一些概念 重儿子:某节点所有儿子中子树最多的儿子 重链:有重儿子构成的链 dfs序:按重儿子优先遍历时 ...

  3. 树剖+线段树||树链剖分||BZOJ1984||Luogu4315||月下“毛景树”

    题面:月下"毛景树" 题解:是道很裸的树剖,但处理的细节有点多(其实是自己线段树没学好).用一个Dfs把边权下移到点权,用E数组记录哪些边被用到了:前三个更新的操作都可以合并起来, ...

  4. 小清的树链剖分10题日志01 树链剖分种果子 有你好果子吃的

    声明:由于本人能力尚不优 故无法做出解释文章 此文仅为自己日记 感谢你的阅读 作为队里的数据结构选手 2019西安邀请赛的E题 竟然在有机时的情况下 想到了线段树log^2的拆位做法 但是题目路径把自 ...

  5. 归纳(四):树链剖分

    剖分的意义 能用线段树搞想要的信息. (其实可以只用来求LCA) 需要的东西 七个数组,在两次dfs中处理出来. dfs1: dep[]:深度 fa[]:父亲 son[]:重儿子 siz[]:子树大小 ...

  6. E. Tree(The 2019 ACM-ICPC China Shannxi Provincial Programming Contest)(树链剖分+线段树)

    4000ms 262144K judge:计蒜客 Description Ming and Hong are playing a simple game called nim game. They h ...

  7. 树链剖分(轻重链)入门

    写在前面 仅想学树剖LCA的同学其实不必要了解线段树 前置知识:树形结构,链式前向星(熟练),线段树(熟练),DFS序(熟练),LCA(了解定义) 树链剖分(树剖):将树分解为一条条不相交的,从祖先到 ...

  8. 对LCA、树上倍增、树链剖分(重链剖分长链剖分)和LCT(Link-Cut Tree)的学习

    LCA what is LCA & what can LCA do LCA(Lowest Common Ancestors),即最近公共祖先 在一棵树上,两个节点的深度最浅的公共祖先就是 L ...

  9. 【POJ 3321】Apple Tree(树的dfs序+树状数组)

    传送门 Solution: 我们只需要采用和树链剖分近似的思想--把整个树的dfs序整理出来,排成线型. 这样一个节点的子树肯定是连续的一段,于是乎就可以用树状数组维护单点修改+区间查询的任务了. # ...

最新文章

  1. iphone 字符串
  2. MSP430学习笔记6-动态数码管的显示
  3. 计算机文档插入操作,电脑在word2007文档中插入数学公式的方法
  4. 线程间操作无效 progressBar2线程不能被访问
  5. express-partials与express4.x不兼容问题
  6. 子类重写父类变量_为什么在子类中不重写超类的实例变量
  7. 从汇编角度看待函数调用
  8. react-native 框架升级 安卓第三方插件报错 Android resource linking failed
  9. linux shell基础(1)
  10. vue前端项目上线配置
  11. winhttp 访问https_asp程序使用Microsoft.XMLHTTP对象请求https时出错,使用WinHttp.WinHttpRequest.5.1解决...
  12. 华为海思有多么强大?一起来看看它的成就
  13. 基于熵权法评估某高校各班级整体情况(公式详解+简单工具介绍)
  14. matlab 分块 矩阵 对角 合并
  15. html日志网页,以HTML为表现的日志记录组件
  16. HC-05蓝牙模块遇到的问题与解决方法及实现和手机通信
  17. 十大家用智能监控摄像头品牌排名
  18. 【第六章 | 虚拟存储器】《操作系统 慕课版》课后答案 + 复习
  19. 【光电工程实训】红外测温枪 红外辐射原理 测量影响因素探究标定
  20. ★为什么不要和“穷人”做朋友?

热门文章

  1. 从零开始一起学习SLAM | 点云到网格的进化
  2. 深度好玩!10个有趣且易上手的AI项目(附 Python 源代码)
  3. 10 个省时间的 PyCharm 技巧,提升工作效率,杠杠滴!
  4. 使用Python+OpenCV预测年龄与性别
  5. 用于手语识别的自注意力机制
  6. 第六篇:Feedforward Networks 前向网络
  7. linux下mysql主从复制搭建
  8. “阿里巴巴大数据系统体系”学习笔记-纲领篇
  9. 180615-精度计算BigDecimal
  10. 使用maven profile 构建不同环境引用不同的值