BZOJ3631 松鼠的新家(差分)

题目链接:BZOJ 3631
题解:从节点x走到节点y经过的所有节点都要放一块糖果,树上两个点之间的路径是唯一的,经过lca(x,y),所以可以差分来做。x 和 y 处 +1,lca(x,y) 和 fa[lca(x,y)] 处 -1,从下到上累加答案即可。又因为一条路径的终点是下一条路径的起点,除了a1和an,其他节点处的糖果数都多算了一次,减掉1;an处不用放糖果,也减掉1;

PS:倍增lca好久没写,写出来一直WA……后来又看了hzwer大神的代码,好棒诶,特地粘过来保存一下
我的(我的)
改成hzwer大神的之后(改成hzwer大神的之后)

code

//我的代码(hzwer大神的写在注释里啦)
#include
#include
#include
#include
#include
using namespace std;
int tot=0,n,fa[300005][20],a[300005],dp[300005],head[300005],deep[300005];
struct edge{
    int to,ne;
}e[600005];
void push(int x,int y)
{
    e[++tot].to=y; e[tot].ne=head[x]; head[x]=tot;
    e[++tot].to=x; e[tot].ne=head[y]; head[y]=tot;
}
void dfs(int now,int pre,int dep)
{
    fa[now][0]=pre; deep[now]=dep;
    for(int i=1;i<=18;i++)
     /*if(deep[now]>=bin[i])*/
      fa[now][i]=fa[fa[now][i-1]][i-1];
     /*else break;*/
    for (int i=head[now];i;i=e[i].ne)
    {
        int v=e[i].to;
        if (v!=pre) dfs(v,now,dep+1);
    }
}
int lca(int x,int y)
{
    if (deep[x]for (int i=18;i>=0;i--)
     if (deep[fa[x][i]]>deep[y]) x=fa[x][i];//if (bin[i]&t) x=fa[x][i];
    if (deep[x]>deep[y]) x=fa[x][0];
    if (x==y) return x;
    for (int i=18;i>=0;i--)
     if (fa[x][i]!=fa[y][i])
      x=fa[x][i],y=fa[y][i]; 
    return fa[x][0];
}
void solve(int now)
{
    for (int i=head[now];i;i=e[i].ne)
    {
        int v=e[i].to;
        if (v!=fa[now][0])
        {
            solve(v);
            dp[now]+=dp[v];
        }
    }
}

int main()
{
    /*bin[0]=1;  for(int i=1;i<20;i++) bin[i]=bin[i-1]<<1;*/
    scanf("%d",&n);
    for (int i=1;i<=n;i++)
     scanf("%d",&a[i]);
    for (int i=1;iint x,y;
        scanf("%d%d",&x,&y);
        push(x,y);
    }
    dfs(a[1],0,1);
    for (int i=1;iint t=lca(a[i],a[i+1]); 
        dp[a[i]]++,dp[a[i+1]]++;
        dp[t]--,dp[fa[t][0]]--;
    }
    solve(a[1]);
    for (int i=2;i<=n;i++) dp[a[i]]--;
    for (int i=1;i<=n;i++) printf("%d\n",dp[i]);
    return 0;
}

你可能感兴趣的:(树形dp,差分,差分,树形DP)