CF1254D Tree Queries
考虑分析一次修改的本质是什么:
- 对于一次修改 ,我们删去 ,会出现若干联通块。
- 对于一个大小为 的联通块,每个点加上 。
不妨让每个点加的值从 变成 ,最后乘上一个 即可。
我们记一个点的度数为 。然后考虑两种算法:
- 暴力 :修改时枚举 的所有儿子 ,暴力按上述式子加(子树加,使用修改复杂度 的数据结构维护),最后处理父亲(父亲所在联通块的大小应该是 的字数大小)
- 暴力 :查询时枚举一些点 (当然 ),然后判断 在 的哪个联通块(要么 是 的祖先,要么找到一个 的儿子 ,使得 是 的祖先),然后根据所在联通块计算 对 的贡献是多少。
哦,但是还要注意如果发生过修改 ,那么 的答案最后要加上 。
这两种算法的复杂度分别是 和 ,其中 是你选择枚举的点数, 是你选择的数据结构修改的复杂度。
这并不优。于是常规手段,根号摊平。(以下的查询指计算这个点对其它点的贡献)
- 对于 的点的修改,使用暴力 中的修改。
- 对于 的点的查询,用数据结构算出任何 的 对 的答案。
- 对于 的点的修改,考虑跳过,直接摆烂。
- 对于 的点的查询,枚举这些点(总个数 ),然后计算贡献。
数据结构我们希望做到修改 ,这样才能保证最后根号的复杂度。
根号题用根号 是极好的,我们选择采用序列分块,这个子树加单点和是常规的 时间戳。
在补充说说。暴力 里有一步是对于 要找到一个 ,满足 是 的儿子,也是 的祖先。这一步其实挺像 ,考虑树剖解决:
我们魔改树求 ,在最后一步,两个点都在一条重链的时候,深度小的那个点在重链上的儿子就是答案。
还有一种情况,就是它们中深度小的那个在重链底,这时答案是它的某个轻儿子,我们无法得知。这种情况需要在跳重链的时候,如果某一步出现 的重链顶的父亲是 ,就不跳了,返回 的重链顶就是答案。
const int N = 1.5e5 + 5,SQ = 405; const ll mod = 998244353; int n,m; ll inv_n,add[N]; vector<int> G[N],big; int B,C,st[SQ],ed[SQ],bl[N]; ll sum[SQ],cnt[N]; int dep[N],siz[N],son[N],fa[N]; int dfn[N],top[N],tot = 0; #define Big(i) ((signed)G[i].size() > B) #define rdfn(u) dfn[u] + siz[u] - 1 #define subt(u) dfn[u],rdfn(u) namespace HLC{ void dfs1(int u = 1,int ft = 0){ dep[u] = dep[fa[u] = ft] + (siz[u] = 1); for(int v : G[u]) if(v != ft){ dfs1(v,u); siz[u] += siz[v]; if(siz[v] > siz[son[u]]) son[u] = v; } if(Big(u)) big.push_back(u); } void dfs2(int u = 1,int tp = 1){ dfn[u] = ++tot; top[u] = tp; if(son[u]) dfs2(son[u],tp); for(int v : G[u]) if(!top[v]) dfs2(v,v); } int son_lca(int x,int y){ while(top[x] != top[y]){ if(dep[top[x]] < dep[top[y]]) swap(x,y); if(fa[top[x]] == y) return top[x]; x = fa[top[x]]; } return son[dep[x] < dep[y] ? x : y]; } } using namespace HLC; namespace DS{ void init(){ B = sqrt(n); C = (n - 1) / B + 1; rep(i,1,C){ st[i] = ed[i - 1] + 1; ed[i] = i == C ? n : i * B; rep(j,st[i],ed[i]) bl[j] = i; } } void upd(int l,int r,ll x){ x %= mod; ++r; (cnt[l] += x) %= mod; (cnt[r] += mod - x) %= mod; (sum[bl[l]] += x) %= mod; (sum[bl[r]] += mod - x) %= mod; } ll qry(int p){ ll res = 0; rep(i,1,bl[p] - 1) (res += sum[i]) %= mod; rep(i,st[bl[p]],p) (res += cnt[i]) %= mod; return res; } } using namespace DS; void Upd(int u,ll x){ (add[u] += x) %= mod; if(Big(u)) return; for(int v : G[u]) if(v != fa[u]) DS::upd(subt(v),x * (n - siz[v])); if(u == 1) return; ll y = x * siz[u]; upd(subt(1),y); upd(subt(u),-y); } ll Qry(int u){ ll res = qry(dfn[u]); for(int v : big) if(u != v){ if(!(dfn[v] <= dfn[u] && rdfn(u) <= rdfn(v))) (res += add[v] * siz[v] % mod) %= mod; else (res += add[v] * (n - siz[son_lca(u,v)]) % mod) %= mod; } res = ((inv_n * res % mod) + add[u]) % mod; return (res % mod + mod) % mod; } ll fpow(ll v,int b){ ll res = 1; while(b){ if(b & 1) (res *= v) %= mod; (v *= v) %= mod; b >>= 1; } return res; } signed main(){ read(n,m); inv_n = fpow(n,mod - 2); rep(i,1,n - 1){ int u,v; read(u,v); G[u].push_back(v); G[v].push_back(u); } init(); dfs1(); dfs2(); while(m--){ if(read() == 1){ int x = read(); Upd(x,read()); } else print(Qry(read()),'\n'); } return 0; }
本文作者:One_Zzz
本文链接:https://www.cnblogs.com/zeno6174/p/CF1254D.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步