【Bzoj 3924】[ZJOI2015]幻想乡战略游戏
[ZJOI2015]幻想乡战略游戏
动态点分治
题目链接:https://www.luogu.org/problemnew/show/P3345
参考:https://www.luogu.org/blog/zcysky/solution-p3345
首先对于点分治下去的重心,我们连边后可以得到一个新的树(点分树),树上的每个点可以维护的是这课子树的信息
点分树的深度不会超过log n
对于修改,我们可以从这课新树对应的节点往上更新内容
这题有一个贪心的方法,朝着最优点走会越走越优,反之越差
点分树的节点维护
这颗子树的军队总数 sum
这颗子树的军队到这个点的代价和 dis1
这颗子树的军队到这个点的点分树的父节点的代价和 dis2
对于修改,我们从这个原树节点 对应的 点分树节点 往上更新即可(n log n)
对于查询,我们会发现如果最优点在2个重心之间(其中一个重心是另外一个重心的点分树的儿子)那么儿子重心可能会比父重心差,就不会往儿子重心走了
于是点分树建边时,可以维护一个信息:父重心的原树儿子w(在2个重心之间)
于是查询我们可以判断,如果点分树上,这个点连向儿子的边的w比这个点优,就往这个点的儿子重心走。时间logn
怎么求出一个点的代价和呢?
找到这个点对应点分树上的点u
对于这颗子树的代价,直接+dis1u即可
对于子树外的代价,我们从u往上走
每次走到一个点v,fa为v 的父亲 +(dis_fa-dis2_v)+(sum_fa-sum_v)*dist (dist为fa到u的距离)
时间logn
于是查询总时间为 n log n log n
于是这题就做完了
求两点距离时,可以用到st表找lca,查询为O1
总时间n log^2 n
1 #include<iostream> 2 #include<cstdio> 3 #define ll long long 4 using namespace std; 5 const ll M=2e5; 6 ll n,m; 7 ll read(){ 8 ll rex=0,f=1;char ch=getchar(); 9 while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} 10 while(ch>='0'&&ch<='9'){rex=rex*10+ch-'0';ch=getchar();} 11 return rex*f; 12 } 13 struct P{ll to,ne,w;}; 14 struct Lca{ 15 ll num,tot,pw[23],L[M],head[M],dep[M],fir[M],f[M<<1][23]; 16 P e[M<<1]; 17 void dfs(ll u,ll fa,ll w){ 18 f[fir[u]=++tot][0]=u;dep[u]=dep[fa]+w; 19 for(ll i=head[u];i;i=e[i].ne){ 20 ll v=e[i].to; 21 if(v!=fa){dfs(v,u,e[i].w);f[++tot][0]=u;} 22 } 23 } 24 void RMQ(){ 25 pw[0]=1;L[1]=0; 26 for(ll i=1;i<=20;++i)pw[i]=pw[i-1]<<1; 27 for(ll i=2;i<=tot;++i)L[i]=L[i>>1]+1; 28 for(ll j=1;j<=20;++j){ 29 for(ll i=1;i+pw[j]-1<=tot;++i){ 30 f[i][j]=dep[f[i][j-1]]<dep[f[i+pw[j-1]][j-1]]?f[i][j-1]:f[i+pw[j-1]][j-1]; 31 } 32 } 33 } 34 ll ask(ll l,ll r){ 35 l=fir[l],r=fir[r]; 36 if(l>r)swap(l,r); 37 ll k=L[r-l+1]; 38 return dep[f[l][k]]<dep[f[r-pw[k]+1][k]]?f[l][k]:f[r-pw[k]+1][k]; 39 } 40 ll dis(ll u,ll v){ 41 return dep[u]+dep[v]-2*dep[ask(u,v)]; 42 } 43 void solve(){ 44 n=read(),m=read(); 45 for(ll i=1,u,v,w;i<n;++i){ 46 u=read(),v=read(),w=read(); 47 e[++num]=(P){v,head[u],w};head[u]=num; 48 e[++num]=(P){u,head[v],w};head[v]=num; 49 } 50 dfs(1,0,0);RMQ(); 51 } 52 }s; 53 struct Divide{ 54 ll num,size,minn,rt,head[M],siz[M],f[M],sum[M],dis1[M],dis2[M]; 55 bool vis[M]; 56 P e[M<<1]; 57 void add(ll u,ll v,ll w){ 58 e[++num]=(P){v,head[u],w};head[u]=num; 59 } 60 void getrt(ll u,ll fa){ 61 ll ma=0;siz[u]=1; 62 for(ll i=s.head[u];i;i=s.e[i].ne){ 63 ll v=s.e[i].to; 64 if(v!=fa&&!vis[v]){ 65 getrt(v,u); 66 siz[u]+=siz[v]; 67 ma=max(ma,siz[v]); 68 } 69 } 70 ma=max(ma,size-siz[u]); 71 if(ma<minn){minn=ma,rt=u;} 72 } 73 void work(ll u){ 74 vis[u]=1; 75 for(ll i=s.head[u];i;i=s.e[i].ne){ 76 ll v=s.e[i].to;if(vis[v])continue; 77 minn=1e12; 78 size=siz[v];getrt(v,0); 79 add(u,rt,v);f[rt]=u; 80 work(rt); 81 } 82 } 83 void update(ll u,ll v){ 84 sum[u]+=v; 85 for(ll i=u;f[i];i=f[i]){ 86 ll dist=s.dis(u,f[i]); 87 dis1[f[i]]+=dist*v; 88 dis2[i]+=dist*v; 89 sum[f[i]]+=v; 90 } 91 } 92 ll calc(ll u){ 93 ll ans=dis1[u]; 94 for(ll i=u;f[i];i=f[i]){ 95 ll dist=s.dis(f[i],u); 96 ans+=dis1[f[i]]-dis2[i]; 97 ans+=dist*(sum[f[i]]-sum[i]); 98 } 99 return ans; 100 } 101 ll query(ll u){ 102 ll ans=calc(u); 103 for(ll i=head[u];i;i=e[i].ne){ 104 ll tmp=calc(e[i].w); 105 ll v=e[i].to; 106 if(tmp<ans)return query(v); 107 } 108 return ans; 109 } 110 void solve(){ 111 minn=1e12;size=n;getrt(1,0); 112 ll la=rt;work(rt); 113 for(ll i=1,u,v;i<=m;++i){ 114 u=read(),v=read(); 115 update(u,v); 116 printf("%lld\n",query(la)); 117 } 118 } 119 }t; 120 int main(){ 121 s.solve(); 122 t.solve(); 123 return 0; 124 }