BZOJ3924: [Zjoi2015]幻想乡战略游戏

BZOJ3924: [Zjoi2015]幻想乡战略游戏

https://lydsy.com/JudgeOnline/problem.php?id=3924

分析:

  • 首先有一个很棒的思路,就是在点分树上跳重心,每次向答案最小的子分治中心移动。
  • 这个是对的,因为树上带权重心往旁边的点走答案一定更差。
  • 只需要每次能够\(O(logn)​\)查询即可,维护点分树上子树\(dist\times 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\n",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
*/
posted @ 2018-12-23 16:54  fcwww  阅读(157)  评论(0编辑  收藏  举报