BZOJ3631 松鼠的新家(差分)

来源:互联网 发布:管家婆sql2005数据库 编辑:程序博客网 时间:2024/04/29 04:02

题目链接: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<iostream>#include<cstdio>#include<cstring>#include<algorithm>#include<cmath>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]<deep[y]) swap(x,y);     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;i<n;i++)    {        int x,y;        scanf("%d%d",&x,&y);        push(x,y);    }    dfs(a[1],0,1);    for (int i=1;i<n;i++)    {        int 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;}