BZOJ3924: [Zjoi2015]幻想乡战略游戏
https://lydsy.com/JudgeOnline/problem.php?id=3924
分析:
- 首先有一个很棒的思路,就是在点分树上跳重心,每次向答案最小的子分治中心移动。
- 这个是对的,因为树上带权重心往旁边的点走答案一定更差。
- 只需要每次能够(O(logn))查询即可,维护点分树上子树(dist imes d)的和。
- 时间复杂度(O(nlogn^2度数))
代码:
#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
using namespace std;
typedef long long ll;
#define N 100050
#define db(x) cerr<<#x<<" = "<<x<<endl
int head[N],to[N<<1],nxt[N<<1],cnt,n,m,val[N<<1];
int siz[N],fk[N],tot,root,dep[N],fa[N][20],used[N];
int sz[N],f[N<<1];
ll dis[N][20],sum[N],sum2[N];
inline void add(int u,int v,int w=0) {
to[++cnt]=v; nxt[cnt]=head[u]; head[u]=cnt; val[cnt]=w;
}
char buf[100000],*p1,*p2;
#define nc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
int rd() {
int x=0,f=1; char s=nc();
while(s<'0') {if(s=='-') f=-1;s=nc();}
while(s>='0') x=(((x<<2)+x)<<1)+s-'0',s=nc();
return x*f;
}
void gr(int x,int y) {
int i;
siz[x]=1; fk[x]=0;
for(i=head[x];i;i=nxt[i]) if(to[i]!=y&&!used[to[i]]) {
gr(to[i],x); siz[x]+=siz[to[i]];
fk[x]=max(fk[x],siz[to[i]]);
}
fk[x]=max(fk[x],tot-siz[x]);
if(fk[x]<fk[root]) root=x;
}
void gd(int x,int y,int rt,int d) {
fa[x][++dep[x]]=rt;
dis[x][dep[x]]=d;
int i;
for(i=head[x];i;i=nxt[i]) if(to[i]!=y&&!used[to[i]]) {
gd(to[i],x,rt,d+val[i]);
}
}
void solve(int x) {
used[x]=1;
int i,all=tot;
gd(x,0,x,0);
for(i=head[x];i;i=nxt[i]) if(!used[to[i]]) {
tot=siz[to[i]]; if(tot>siz[x]) tot=all-siz[x];
root=0; gr(to[i],x); f[i]=root; solve(root);
}
}
void upd(int x,int v) {
int i;
for(i=dep[x];i;i--) {
sum[fa[x][i]]+=dis[x][i]*v;
sum2[fa[x][i]]+=dis[x][i-1]*v;
sz[fa[x][i]]+=v;
}
}
ll calc(int x) {
int i;
ll re=0;
for(i=dep[x];i;i--) {
re+=sum[fa[x][i]]-sum2[fa[x][i+1]]+(sz[fa[x][i]]-sz[fa[x][i+1]])*dis[x][i];
}
return re;
}
int rt;
ll query() {
int x=rt,i;
ll nowans=calc(x);
while(1) {
ll mn=1ll<<60; int p=0;
for(i=head[x];i;i=nxt[i]) if(f[i]) {
ll tmp=calc(to[i]);
if(tmp<mn) mn=tmp,p=f[i];
}
if(mn<nowans) x=p,nowans=calc(x);
else return nowans;
}
}
int main() {
n=rd(),m=rd();
int i,x,y,z;
for(i=1;i<n;i++) {
x=rd(),y=rd(),z=rd(); add(x,y,z); add(y,x,z);
}
fk[0]=1<<30; tot=n; gr(1,0);
rt=root; solve(root);
while(m--) {
x=rd(),y=rd();
upd(x,y);
printf("%lld
",query());
}
}
/*
10 5
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1
*/