lp3676 小清新数据结构题
傳送門
Description
有一棵\(n\)個(gè)點(diǎn)的樹,每個(gè)點(diǎn)有一個(gè)點(diǎn)權(quán)。
現(xiàn)在有\(q\)次操作,每次操作是修改一個(gè)點(diǎn)的點(diǎn)權(quán)或指定一個(gè)點(diǎn),詢問(wèn)以這個(gè)點(diǎn)為根時(shí)每棵子樹點(diǎn)權(quán)和的平方和。
Solution
我們?cè)O(shè)\(Sum=\sum_{i=1}^{n} w_i\),\(s_i\)表示\(i\)子樹的權(quán)值和
發(fā)現(xiàn)不管根是哪個(gè)節(jié)點(diǎn),\(W=\sum_{i=1}^n s_i(Sum-s_i)\)都是一個(gè)定值
因?yàn)樗喈?dāng)于對(duì)于每條邊連接的兩個(gè)聯(lián)通塊的”點(diǎn)權(quán)和的積“的和
所以,我們要求的\(\sum_{i=1}^{n} s_i^2=Sum*(\sum_{i=1}^n s_i)-W\)
考慮怎么計(jì)算\(calc(root)=\sum_{i=1}^{n} s_i\)
發(fā)現(xiàn)其實(shí)上就是\(Sum+\sum_{i=1}^n dis(root,i)*val_i\)
可以用點(diǎn)分樹來(lái)維護(hù)
考慮怎么計(jì)算\(W\)
對(duì)于每次修改\(val_i+=delta\),就有\(W+=\sum_{j=1}^n val_j*dis(i,j)*delta=delta*calc(i)\)
發(fā)現(xiàn)這道題本質(zhì)和幻想鄉(xiāng)戰(zhàn)略游戲是一樣的
可是把原來(lái)的代碼交上去,就\(TLE\)了,只好考慮重構(gòu)
不妨把過(guò)程重新理一遍:
點(diǎn)分樹有一個(gè)性質(zhì),對(duì)于樹上的兩個(gè)點(diǎn),它們的\(lca\)一定在兩點(diǎn)在原樹上的簡(jiǎn)單路徑上
因此,要求兩個(gè)點(diǎn)的實(shí)際距離,可以通過(guò)分別計(jì)算它們到\(lca\)的實(shí)際距離求和得到
考慮以上性質(zhì),我們可以設(shè):
- \(vsum_i\)表示\(i\)在點(diǎn)分樹上的子樹內(nèi)的權(quán)值和
- \(dis1_i\)表示 \(\sum_{j} dis(i,j)*val_j\),\(j\)是\(i\)在點(diǎn)分樹上的子樹內(nèi)的節(jié)點(diǎn)
- \(dis2_i\)表示 \(\sum_{j} dis(par_i,j)*val_j\)
- \(par_i\)是\(i\)在點(diǎn)分樹上的父親
\(dis1_i=\sum_{son} dis2_son\),\(son\)是\(i\)在點(diǎn)分樹上的兒子
\(calc(i)\)其實(shí)上就是枚舉\(lca\)
\(lca=i\)時(shí),和為\(dis1_i\)
\((\sum_{lca=par_j} dis1_{par_j}-dis2_j)\)算的是外圍節(jié)點(diǎn)到\(lca\)的和
\(dis(i,par_j)*(vsum_{par_j}-vsum_j))\)算的是它們到\(i\)的和
我們發(fā)現(xiàn),求距離的部分其實(shí)不需要像之前那樣全部記下來(lái),可以用\(RMQ\)求樹上距離
具體來(lái)說(shuō),和\(RMQ\)求\(LCA\)差不多,只不過(guò)維護(hù)的最小值不是歐拉序,而是到根路徑的長(zhǎng)
Code?
#include<bits/stdc++.h> #define ll long long #define max(a,b) ((a)>(b)?(a):(b)) #define min(a,b) ((a)<(b)?(a):(b)) #define reg register #define int llinline int read() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}return x*f; } const int MN=2e5+5;int n,m; struct edge{int to,w,nex;}e[MN<<2];int en,hr[MN]; void ins(int x,int y,int w,int *h){e[++en]=(edge){y,w,h[x]};h[x]=en;} int Val[MN],O,SUM;struct Tree {int Hr[MN],ind,st[MN<<2][21],dep[MN],pos[MN<<1],lg[MN<<2],_siz[MN];inline void Ins(int x,int y,int w){ins(x,y,w,Hr);ins(y,x,w,Hr);}int dis(int x,int y){if(pos[x]>pos[y]) std::swap(x,y);reg int k=lg[pos[y]-pos[x]+1];return dep[x]+dep[y]-2*min(st[pos[x]][k],st[pos[y]-(1<<k)+1][k]);}void dfs(int x,int fa=0){st[pos[x]=++ind][0]=dep[x];reg int i;for(i=Hr[x];i;i=e[i].nex)if(e[i].to^fa)dep[e[i].to]=dep[x]+e[i].w,dfs(e[i].to,x),st[++ind][0]=dep[x];}void get_O(int x=1,int fa=0){reg int i;_siz[x]=Val[x];for(i=Hr[x];i;i=e[i].nex)if(e[i].to^fa)get_O(e[i].to,x),_siz[x]+=_siz[e[i].to];O+=1ll*(SUM-_siz[x])*_siz[x];}inline void pre_work(){reg int i,j;get_O();dfs(1);for(lg[0]=-1,i=1;i<(MN<<2);i++)lg[i]=lg[i>>1]+1;for(j=1;j<20;++j)for(i=1;i+(1<<j)-1<=ind&&i<=ind;++i)st[i][j]=min(st[i][j-1],st[i+(1<<(j-1))][j-1]);}}T;int sum,rt,mx[MN],vis[MN],par[MN],cnt,siz[MN]; ll dis1[MN],dis2[MN],sumv[MN];void getrt(int x,int fa) {siz[x]=1;mx[x]=0;reg int i;for(i=T.Hr[x];i;i=e[i].nex)if(!vis[e[i].to]&&(e[i].to!=fa))getrt(e[i].to,x),siz[x]+=siz[e[i].to],mx[x]=max(mx[x],siz[e[i].to]);mx[x]=max(mx[x],sum-siz[x]);if(mx[x]<mx[rt]) rt=x; }void _work(int x,int fa) {vis[x]=1;par[x]=fa;reg int i;for(i=T.Hr[x];i;i=e[i].nex)if(!vis[e[i].to])mx[rt=0]=sum=siz[e[i].to],getrt(e[i].to,0),ins(x,rt,0,hr),_work(rt,x); }void pre_work() {sum=mx[rt=0]=n;getrt(1,0);int tmp=rt;_work(rt,0);rt=tmp; }inline void Modify(int x,int val) {sumv[x]+=val;reg int i,dis;for(i=x;par[i];i=par[i]){dis=T.dis(par[i],x);dis1[par[i]]+=dis*val;dis2[i]+=dis*val;sumv[par[i]]+=val;} }inline ll calc(int x) {ll ans=dis1[x];reg int i,dis;for(i=x;par[i];i=par[i]){dis=T.dis(par[i],x);ans+=dis1[par[i]]-dis2[i];ans+=dis*(sumv[par[i]]-sumv[i]);}return ans; }signed main() {n=read();m=read();reg int i,x,y;for(i=1;i<n;i++) x=read(),y=read(),T.Ins(x,y,1);pre_work();for(i=1;i<=n;++i) SUM+=Val[i]=read();T.pre_work();for(i=1;i<=n;++i) Modify(i,Val[i]);while(m--){reg int opt=read();if(opt==1){x=read();y=read()-Val[x];Modify(x,y);SUM+=y;O+=y*calc(x);Val[x]+=y;}else printf("%lld\n",SUM*(calc(read())+SUM)-O);}return 0; }Blog來(lái)自PaperCloud,未經(jīng)允許,請(qǐng)勿轉(zhuǎn)載,TKS!
轉(zhuǎn)載于:https://www.cnblogs.com/PaperCloud/p/10664448.html
總結(jié)
以上是生活随笔為你收集整理的lp3676 小清新数据结构题的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。