1. 程式人生 > >bzoj3924 [Zjoi2015]幻想鄉戰略遊戲(動態點分治)

bzoj3924 [Zjoi2015]幻想鄉戰略遊戲(動態點分治)

就是求帶權重心,可以修改點權。
我們首先建出重心樹。對於每個節點x記
s1[x]–x的子樹到x的答案,
s2[x]–x的子樹的點權和,
s3[x]–x的子樹到fa[x]的答案。
那我們就可以通過這些資訊得出以x為重心的答案(在重心樹上一直往上跳,複雜度O(log n).)
修改點權時,最多影響logn個點,我們就O(logn)的更新一下這些點的資訊。
怎麼找帶權重心呢?從重心樹的根開始,列舉它的所有出邊(原樹中的邊),如果往那個方向走得到的答案更優,就走到那個方向的重心樹中的兒子上去,否則最優的就是當前點了。(可以證明最多隻有一個方向比我目前的點優,如果一個方向緊鄰我的點的答案比我大,則這個方向的所有點的答案都肯定比我大。)
複雜度

O(nlog2n20)

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <queue>
using namespace std;
#define ll long long
#define inf 0x3f3f3f3f
#define N 100010
inline char gc(){
    static char buf[1<<16],*S,*T;
    if(S==T){T=(S=buf)+fread(buf,1,1<<16
,stdin);if(T==S) return EOF;} return *S++; } inline 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*10+ch-'0',ch=getchar(); return x*f; } int n,m,fa[N],sz[N],f[N],sumsz,rt,h[N],num=0,root,dis[N]; int
dep[N],mn[N<<1][20],dfn[N],dfnum=0,Log[N<<1]; ll s1[N],s2[N],s3[N];//s1[x]--x的子樹到x的答案,s2[x]--x的子樹的點權和,s3[x]--x的子樹到fa[x]的答案 struct edge{ int to,next,val; }data[N<<1]; bool vis[N]; vector<int>son[N],Gson[N]; inline void dfs(int x,int Fa){ mn[++dfnum][0]=x;dfn[x]=dfnum; for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(y==Fa) continue; dep[y]=dep[x]+1;dis[y]=dis[x]+data[i].val; dfs(y,x);mn[++dfnum][0]=x; } } inline void inirmq(){ Log[0]=-1; for(int i=1;i<=n<<1;++i) Log[i]=Log[i>>1]+1; for(int i=1;i<=Log[n<<1];++i) for(int j=1;j<=2*n-1;++j){ if(j+(1<<i-1)>2*n-1) break; mn[j][i]=dep[mn[j][i-1]]<dep[mn[j+(1<<i-1)][i-1]]?mn[j][i-1]:mn[j+(1<<i-1)][i-1]; } } inline void dfs1(int x,int Fa){ sz[x]=1; for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(vis[y]||y==Fa) continue; dfs1(y,x);sz[x]+=sz[y]; } } inline void dfs2(int x,int Fa){ f[x]=0; for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(vis[y]||y==Fa) continue; dfs2(y,x);f[x]=max(f[x],sz[y]); }f[x]=max(f[x],sumsz-sz[x]);if(f[x]<f[rt]) rt=x; } inline void getG(int x){ vis[x]=1;dfs1(x,0); for(int i=h[x];i;i=data[i].next){ int y=data[i].to;if(vis[y]) continue; sumsz=sz[y];rt=0;dfs2(y,0);fa[rt]=x; son[x].push_back(y);Gson[x].push_back(rt);getG(rt); } } inline int lca(int x,int y){ x=dfn[x],y=dfn[y];if(x>y) swap(x,y); int t=Log[y-x+1]; return dep[mn[x][t]]<dep[mn[y-(1<<t)+1][t]]?mn[x][t]:mn[y-(1<<t)+1][t]; } inline int caldis(int x,int y){ return dis[x]+dis[y]-2*dis[lca(x,y)]; } inline void change(int x,int val){ for(int i=x;i;i=fa[i]){ s1[i]+=(ll)caldis(i,x)*val; s2[i]+=val; if(fa[i]) s3[i]+=(ll)caldis(x,fa[i])*val; } } inline ll calc(int x){ ll res=s1[x]; for(int i=x;fa[i];i=fa[i]){ res+=s1[fa[i]]-s3[i]+(s2[fa[i]]-s2[i])*caldis(x,fa[i]); }return res; } inline ll ask(int x){ ll res=calc(x); for(int i=0;i<son[x].size();++i){ int y=son[x][i]; if(calc(y)<res) return ask(Gson[x][i]); }return res; } int main(){ // freopen("a.in","r",stdin); n=read();m=read();f[0]=inf; for(int i=1;i<n;++i){ int x=read(),y=read(),val=read(); data[++num].to=y;data[num].next=h[x];h[x]=num;data[num].val=val; data[++num].to=x;data[num].next=h[y];h[y]=num;data[num].val=val; }dfs(1,0);inirmq(); dfs1(1,0);sumsz=n;rt=0;dfs2(1,0);fa[rt]=0;root=rt;getG(rt); while(m--){ int x=read(),val=read(); change(x,val);printf("%lld\n",ask(root)); }return 0; }