bzoj3631[JLOI2014]松鼠的新家

题目:https://www.lydsy.com/JudgeOnline/problem.php?id=3631

树上差分。

因为从根到当前点的差分有种种不方便(需要改孩子们的值;需要处理路径上拐出去的其他边等),所以从叶子到根方向地差分!

注意一下起点不+1,终点+1。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=3e5+5;
int n,t[N],head[N],xnt,g[N],fa[N][20],dep[N],ans[N];
struct Edge{
    int next,to;
    Edge(int n=0,int t=0):next(n),to(t) {}
}edge[N<<1];
void add(int x,int y)
{
    edge[++xnt]=Edge(head[x],y);head[x]=xnt;
    edge[++xnt]=Edge(head[y],x);head[y]=xnt;
}
void dfs(int cr,int f,int d)
{
    fa[cr][0]=f;dep[cr]=d;
    for(int i=1;fa[fa[cr][i-1]][i-1];i++)fa[cr][i]=fa[fa[cr][i-1]][i-1];
    for(int i=head[cr];i;i=edge[i].next)
        if(edge[i].to!=f)dfs(edge[i].to,cr,d+1);
}
int lg(int a){int cnt;while(a)a>>=1,cnt++;return cnt;}
void solve(int a,int b)
{
    g[fa[a][0]]++;g[b]++;
//    printf("g[%d]=%d g[%d]=%d\n",a,g[a],b,g[b]);
    if(dep[a]<dep[b])swap(a,b);int d=dep[a]-dep[b],j=0;
    while(d)
    {
        if(d&1)a=fa[a][j];d>>=1;j++;
    }
    if(a==b){g[a]--;g[fa[a][0]]--;return;}
    for(int j=lg(dep[a]);j>=0;j--)
        if(fa[a][j]!=fa[b][j])a=fa[a][j],b=fa[b][j];
    if(a!=b)a=fa[a][0],b=fa[b][0];
    g[a]--;g[fa[a][0]]--;
//    printf("g[%d]=%d g[%d]=%d\n",a,g[a],fa[a][0],g[fa[a][0]]);printf("lca=%d\n",a);
}
int dfs2(int cr,int f)
{
    int tmp=g[cr];
    for(int i=head[cr],v;i;i=edge[i].next)
        if((v=edge[i].to)!=f)
            tmp+=dfs2(v,cr);
    ans[cr]=tmp;return tmp;
}
int main()
{
    scanf("%d",&n);int x,y;
    for(int i=1;i<=n;i++)scanf("%d",&t[i]);
    for(int i=1;i<n;i++)
        scanf("%d%d",&x,&y),add(x,y);
    dfs(1,0,1);
    for(int i=2;i<=n;i++){
        solve(t[i-1],t[i]);
//        for(int i=1;i<=n;i++)printf("%d ",g[i]);printf("\n");
    }
    dfs2(1,0);ans[t[1]]++;ans[t[n]]--;
    for(int i=1;i<=n;i++)printf("%d\n",ans[i]);
    return 0;
}

猜你喜欢

转载自www.cnblogs.com/Narh/p/9168053.html
今日推荐