BZOJ 3924: [Zjoi2015]幻想乡战略游戏 动态点分治

细节挺多的,但是也确实加深了我对动态点分治的理解. 

这段代码值得关注: 

ll query(int u) 
{
    ll re=calc(u);        
    for(int i=hd[u];i;i=nex[i])
    {
    	ll t=calc(to[i]);
    	if(t<re) return query(hto[i]);    
    }
    return re;     
}

 

点分树的结构是和原树不同的. 

我们知道,最优决策点和根节点的连线上的点的答案一定是越来越优的. 

而如果发现 $to[i]$ 所在子树中,$to[i]$ 更优,那么想在点分树中到达 $to[i]$,直接走到 $to[i]$所在重心即可. 

开始的时候思维太僵化了,一直想的是只靠重心之间转移,但其实我们也要将点分树和原树结合起来. 

#include <cstdio> 
#include <vector>    
#include <algorithm>  
#define N 100004
#define inf 1000000000000000
#define ll long long  
#define setIO(s) freopen(s".in","r",stdin) , freopen(s".out","w",stdout)
using namespace std; 
int edges,n;  
int hd[N],to[N<<1],nex[N<<1],val[N<<1], hto[N<<1];   
void add(int u,int v,int c)
{
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v,val[edges]=c; 
}
namespace tree
{    
    ll dis[N]; 
    int fa[N],top[N],size[N],son[N],dep[N];
    void dfs1(int u,int ff)
    { 
        fa[u]=ff,size[u]=1; 
        for(int i=hd[u];i;i=nex[i]) 
            if(to[i]!=ff) 
            {
                int v=to[i];  
                dep[v]=dep[u]+1,dis[v]=dis[u]+1ll*val[i]; 
                dfs1(v,u);  
                size[u]+=size[v];
                if(size[v]>size[son[u]]) son[u]=v; 
            }
    } 
    void dfs2(int u,int tp)
    {
        top[u]=tp; 
        if(son[u]) dfs2(son[u],tp); 
        for(int i=hd[u];i;i=nex[i]) 
            if(to[i]!=fa[u]&&to[i]!=son[u]) 
                dfs2(to[i],to[i]);      
    }
    int LCA(int x,int y)
    {
        while(top[x]!=top[y]) 
            dep[top[x]]>dep[top[y]]?x=fa[top[x]]:y=fa[top[y]]; 
        return dep[x]<dep[y]?x:y; 
    }
    ll Dis(int x,int y)
    {
        return dis[x]+dis[y]-2ll*dis[LCA(x,y)];    
    }
}; 
vector<int>e[N];   
int root,sn;
int Fa[N],mx[N],size[N],vis[N];
ll F[N],G[N],num[N],tmp;   
void getroot(int u,int ff)
{
    size[u]=1,mx[u]=0;  
    for(int i=hd[u];i;i=nex[i]) 
        if(to[i]!=ff&&!vis[to[i]]) 
            getroot(to[i],u),size[u]+=size[to[i]],mx[u]=max(mx[u],size[to[i]]);
    mx[u]=max(mx[u],sn-size[u]); 
    if(mx[u]<mx[root]) root=u;
} 
void dfs(int u,int ff)
{
    size[u]=1; 
    for(int i=hd[u];i;i=nex[i]) 
        if(to[i]!=ff&&!vis[to[i]]) 
            dfs(to[i],u),size[u]+=size[to[i]]; 
}
void prepare(int u)
{
    vis[u]=1; 
    for(int i=hd[u];i;i=nex[i]) 
        if(!vis[to[i]]) 
        {
            dfs(to[i],u),root=0,sn=size[to[i]],getroot(to[i],u),hto[i]=root,Fa[root]=u,prepare(root);        
        }
}
void Update(int u,ll v)
{ 
    int U=u; 
    num[u]+=v; 
    for(;Fa[u];u=Fa[u]) 
    {
        F[Fa[u]]+=v*tree::Dis(U,Fa[u]);        
        G[u]+=v*tree::Dis(U,Fa[u]);      
        num[Fa[u]]+=v; 
    }
}      
ll calc(int u) 
{
    ll re=F[u];     
    int U=u; 
    for(;Fa[u];u=Fa[u])
    { 
        re+=F[Fa[u]]-G[u]+1ll*(num[Fa[u]]-num[u])*tree::Dis(U,Fa[u]);      
    } 
    return re;     
}
ll query(int u) 
{
    ll re=calc(u);        
    for(int i=hd[u];i;i=nex[i])
    {
    	ll t=calc(to[i]);
    	if(t<re) return query(hto[i]);    
    }
    return re;     
}
int main()  
{
    int i,j,Q; 
    //  setIO("input");   
    scanf("%d%d",&n,&Q); 
    for(i=1;i<n;++i) 
    {
        int a,b,c; 
        scanf("%d%d%d",&a,&b,&c),add(a,b,c),add(b,a,c); 
    } 
    int cc=0; 
    tree::dfs1(1,0); 
    tree::dfs2(1,1);   
    sn=n,mx[0]=n,root=0,getroot(1,0),cc=root,prepare(root); 
    for(int cas=1;cas<=Q;++cas)
    {
        int u,e; 
        scanf("%d%d",&u,&e), Update(u,1ll*e);  
        printf("%lld\n",query(cc));     
    }
    return 0; 
}

  

posted @ 2019-09-03 10:30  EM-LGH  阅读(117)  评论(0编辑  收藏  举报