Luogu3345 [ZJOI2015]幻想乡战略游戏

Luogu3345 [ZJOI2015]幻想乡战略游戏

题面:良心洛谷

解析

动态点分治。假定我们已经确定了补给点,可以通过在点分树上\(O(logn)\)统计答案,修改亦可\(O(logn)\)做到,具体的话就是维护一下当前重心\(w\)\(\sum_{v} d_v\)\(\sum_{v} d_vdist(v,w)\),在多维护一个父节点信息,容斥即可。现在考虑如何确定补给点?还是在点分树上操作(因为这样能保证复杂度为\(O(logn)\),我们视度数为常数),每一次选最优的子树向下走即可,若走不动,那么当前点就是答案了。

代码


// luogu-judger-enable-o2
#include<cmath>
#include<cstdio>
#include<iostream>
#define N 100005
#define LL long long
using namespace std;
const int nlog=17;
inline int In(){
    char c=getchar(); int x=0,ft=1;
    for(;c<'0'||c>'9';c=getchar()) if(c=='-') ft=-1;
    for(;c>='0'&&c<='9';c=getchar()) x=x*10+c-'0';
    return x*ft;
}
inline int max(int a,int b){
    return a>b?a:b;
}
int n,m,h[N],e_tot=0;
struct Edge{ int to,nex,d; }e[N<<1];
inline void add(int u,int v,int w){
    e[++e_tot]=(Edge){v,h[u],w}; h[u]=e_tot;
}
int d[N],p[N<<1][nlog+5],dfn[N],dfs_clock=0;
void dfs(int u,int pre,int dep){
    d[u]=dep; p[++dfs_clock][0]=u; dfn[u]=dfs_clock;
    for(int i=h[u],v;i;i=e[i].nex){
        v=e[i].to; if(v==pre) continue;
        dfs(v,u,dep+e[i].d); p[++dfs_clock][0]=u;
    }
}
int _p[nlog+5];
inline void get_st(){
    _p[0]=1; for(int i=1;i<=nlog;++i) _p[i]=_p[i-1]*2;
    for(int j=1;j<=nlog;++j)
    for(int i=1;i+_p[j]<=dfs_clock;++i)
    p[i][j]=d[p[i][j-1]]<d[p[i+_p[j-1]][j-1]]
    ?p[i][j-1]:p[i+_p[j-1]][j-1];
}
inline int LCA(int x,int y){
    if(dfn[x]>dfn[y]) swap(x,y);
    int k=log(dfn[y]-dfn[x]+1)/log(2);
    return d[p[dfn[x]][k]]<d[p[dfn[y]-_p[k]+1][k]]?
    p[dfn[x]][k]:p[dfn[y]-_p[k]+1][k];
}
inline int dist(int x,int y){
    return d[x]+d[y]-2*d[LCA(x,y)];
}
int Sz,Sp_root,root,sz[N],f[N]; bool vis[N];
void get_rt(int u,int pre){
    sz[u]=1; f[u]=0;
    for(int i=h[u],v;i;i=e[i].nex){
        v=e[i].to; if(vis[v]||v==pre) continue;
        get_rt(v,u); sz[u]+=sz[v]; f[u]=max(f[u],sz[v]);
    }
    f[u]=max(f[u],Sz-sz[u]); if(f[u]<f[root]) root=u;
}
int fa[N],_g[N][nlog+5];
void Solve(int u,int pre,int dep){
    vis[u]=1; fa[u]=pre; int Oth_sz=Sz-sz[u];
    for(int i=h[u],v;i;i=e[i].nex){
        v=e[i].to; if(vis[v]) continue;
        Sz=sz[v]>sz[u]?Oth_sz:sz[v]; root=0;
        get_rt(v,u); _g[v][dep]=root;
        Solve(root,u,dep+1);
    }
}
int sz_o[N]; LL sum_o[N],sum_f[N];
inline void Modify(int u,int C){
    sz_o[u]+=C;
    for(int p=fa[u],l_p=u,dis;p!=-1;p=fa[l_p=p]){
        dis=dist(u,p); sz_o[p]+=C;
        sum_o[p]+=1ll*C*dis; sum_f[l_p]+=1ll*C*dis;
    }
}
inline LL Query(int u){
    LL ans=0; ans+=sum_o[u];
    for(int p=fa[u],l_p=u,dis,y;p!=-1;p=fa[l_p=p]){
        dis=dist(u,p); ans+=1ll*dis*(sz_o[p]-sz_o[l_p]);
        ans+=sum_o[p]; ans-=sum_f[l_p];
    }
    return ans;
}
inline LL get_ans(){
    LL m_ans,S; int u=Sp_root,y;
    for(int d=0;;++d){
        m_ans=Query(u); y=u;
        for(int i=h[u],v;i;i=e[i].nex){
            v=e[i].to; if(!_g[v][d]) continue;
            if(S=Query(v)<m_ans) m_ans=S,y=_g[v][d];
        }
        if(y==u) break; u=y;
    }
    return m_ans;
}
int main(){
    n=In(); m=In();
    for(int i=1,u,v,w;i<n;++i){
        u=In(); v=In(); w=In();
        add(u,v,w); add(v,u,w);
    }
    dfs(1,0,0); get_st();
    Sz=f[0]=n; root=0; get_rt(1,-1);
    Sp_root=root; Solve(root,-1,0);
    for(int i=1,u,val;i<=m;++i){
        u=In(); val=In(); Modify(u,val);
        printf("%lld\n",get_ans());
    }
    return 0;
}


posted @ 2019-03-19 22:07  pkh68  阅读(150)  评论(0编辑  收藏  举报