LuoguP4183 [USACO18JAN]Cow at Large P 点分治+树状数组+prufer序列

非常好的一道题.          

假设当前要求 $ans[x]$.    

先令 $x$ 为根,然后发现对于子树 $y$ 来说,令 $g[y]$ 表示距离 $y$ 最近的叶子节点.         

若 $g[y] \leqslant dis(x,y) $ 则 $y$ 子树的叶子中选一个就可以防止 $x$ 走到 $y$ 的子树中.   

那么这个时候的答案就是 $\sum [g[i] \leqslant dis(x,i)][g[fa_{i}] > dis(x,fa_{i}]$.

不难发现如果 $x$ 满足条件,则 $x$ 的所有儿子也都满足条件,即满足条件的一定是一整颗子树.     

由于我们只想让一颗子树贡献一次,我们就可以考虑使用 prufer 序列.   

prufer 序列有:$\sum deg[x]-1=n-2$,则有 $2=\sum 2-deg[x]$,而由于算根节点的度数的时候没有减1,所以有 $1=\sum 2-deg[x]$.    

所以对于 $x$ 为根的所有 $y$ 来说,如果 $y$ 满足条件,$y$ 的贡献就是 $2-deg[y]$.     

我们就将问题转化为 $ans[x]=\sum [g[i] \leqslant dis(i,x)](2-deg[i])$,这个用点分治+树状数组统计就行了.     

code:   

#include <cstdio>   
#include <cstring>
#include <algorithm>          
#define ll long long 
#define N 70009   
#define setIO(s) freopen(s".in","r",stdin) 
using namespace std;
int n,edges,sn,root;   
int hd[N],to[N<<1],nex[N<<1];   
int deg[N],g[N],dep[N];   
int size[N],mx[N],vis[N],ans[N];   
void add(int u,int v) {  
    nex[++edges]=hd[u],hd[u]=edges,to[edges]=v; 
}
struct BIT {   
    #define M 150000
    int C[M];      
    int lowbit(int x) { return x&(-x); }  
    void update(int x,int v) {         
        for(int i=x;i<M;i+=lowbit(i)) 
            C[i]+=v;  
    }
    int query(int x) {      
        int re=0; 
        for(int i=x;i;i-=lowbit(i)) 
            re+=C[i];  
        return re;  
    }
    void clr(int x) {     
        for(int i=x;i<N;i+=lowbit(i)) 
            C[i]=0;     
    }
    #undef M 
}ope;         
void dfs1(int x,int ff) {  
    g[x]=N;    
    for(int i=hd[x];i;i=nex[i]) 
        if(to[i]!=ff) {
            dfs1(to[i],x);    
            g[x]=min(g[x],g[to[i]]+1);   
        }
    if(deg[x]==1) g[x]=0;  
}
void dfs2(int x,int ff) {  
    if(ff) g[x]=min(g[x],g[ff]+1);         
    for(int i=hd[x];i;i=nex[i]) 
        if(to[i]!=ff)       
            dfs2(to[i],x);         
}
void getroot(int x,int ff) {  
    size[x]=1,mx[x]=0;   
    for(int i=hd[x];i;i=nex[i]) {  
        int y=to[i];  
        if(vis[y]||y==ff) continue;   
        getroot(y,x);  
        size[x]+=size[y];  
        mx[x]=max(mx[x],size[y]);  
    }
    mx[x]=max(mx[x],sn-size[x]);     
    if(mx[x]<mx[root]) root=x;     
}            
void ins(int x,int ff,int t) {     
    dep[x]=dep[ff]+1;   
    ope.update(N+g[x]-dep[x],t*(2-deg[x]));         
    for(int i=hd[x];i;i=nex[i]) {  
        int y=to[i]; 
        if(y==ff||vis[y]) continue;   
        ins(y,x,t);    
    }
}             
void upd(int x,int ff) {   
    dep[x]=dep[ff]+1;  
    ans[x]+=ope.query(N+dep[x]);     
    for(int i=hd[x];i;i=nex[i]) { 
        int y=to[i]; 
        if(y==ff||vis[y]) continue;   
        upd(y,x);   
    }
}
void DEL(int x,int ff) {      
    ope.clr(N+g[x]-dep[x]);      
    for(int i=hd[x];i;i=nex[i]) {   
        if(to[i]!=ff&&!vis[to[i]]) DEL(to[i],x);  
    }
}
void calc(int x) {         
    dep[0]=-1,ins(x,0,1);  
    ans[x]+=ope.query(N);           
    for(int i=hd[x];i;i=nex[i]) {   
        int y=to[i];  
        if(vis[y]) continue;      
        dep[x]=0;  
        ins(y,x,-1);       
        upd(y,x);   
        ins(y,x, 1);    
    }
    dep[0]=-1,ins(x,0,-1);      
}
void solve(int x) {          
    calc(x);  
    vis[x]=1;       
    for(int i=hd[x];i;i=nex[i]) { 
        int y=to[i];  
        if(vis[y]) continue;  
        root=0;  
        sn=size[y];     
        getroot(y,x);    
        solve(root); 
    }    
}
int main() {  
    // setIO("input");                          
    scanf("%d",&n);   
    int x,y,z; 
    for(int i=1;i<n;++i) {  
        scanf("%d%d",&x,&y);  
        add(x,y); 
        add(y,x);   
        ++deg[x],++deg[y];    
    }   
    dfs1(1,0);
    dfs2(1,0);            
    sn=n,root=0,mx[root]=N;    
    getroot(1,0);      
    solve(root);    
    for(int i=1;i<=n;++i)             
        if(deg[i]==1) printf("1\n"); 
        else printf("%d\n",ans[i]);   
    return 0;
}

  

posted @ 2020-07-09 10:46  EM-LGH  阅读(157)  评论(0编辑  收藏  举报