洛谷P3258 [JLOI2014]松鼠的新家(树上差分)

题目描述

松鼠的新家是一棵树,前几天刚刚装修了新家,新家有nnn 个房间,并且有 n−1n-1n1 根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。

松鼠想邀请****前来参观,并且还指定一份参观指南,他希望**能够按照他的指南顺序,先去 a1a_1a1,再去 a2a_2a2,……,最后到 ana_nan,去参观新家。可是这样会导致**重复走很多房间,懒惰的**不停地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。

**是个馋家伙,立马就答应了。现在松鼠希望知道为了保证**有糖果吃,他需要在每一个房间各放至少多少个糖果。

因为松鼠参观指南上的最后一个房间 ana_nan 是餐厅,餐厅里他准备了丰盛的大餐,所以当**在参观的最后到达餐厅时就不需要再拿糖果吃了。

输入格式

第一行一个正整数 nnn,表示房间个数第二行 nnn 个正整数,依次描述 a1,a2,⋯ ,ana_1, a_2,\cdots,a_na1,a2,,an

接下来 n−1n-1n1 行,每行两个正整数 x,yx,yx,y,表示标号 xxx 和 yyy 的两个房间之间有树枝相连。

输出格式

一共 nnn 行,第 iii 行输出标号为 iii 的房间至少需要放多少个糖果,才能让**有糖果吃。

输入输出样例

输入 #1
5
1 4 5 3 2
1 2
2 4
2 3
4 5
输出 #1
1
2
1
2
1
可能在一些路径上反复经过,所以考虑树上差分(其实是看见tag了
从a走到b,就是相当于给a到b这条路径上的 a ~ b的前一个节点 的糖数++。为什么是前一个结点呢?因为如果不是这样的话b这个节点的糖数会加重复了比如a~b都++,b~c都++,则b相当于重复了一次,不理解的话可以考虑一下到叶子节点时这种特殊的情况,注意题目也在暗示了“当**在参观的最后到达餐厅时就不需要再拿糖果吃了。”
最后跑一遍DFS求出答案即可。
#include <iostream>
#include <cstdio>
#include <cstring>
#include <queue>
#include <cmath>
#include <algorithm>
using namespace std;
const int SIZE=300005;
int f[SIZE][22],d[SIZE],vis[SIZE],lg[SIZE];
int ver[SIZE*2],Next[SIZE*2],head[SIZE];
int diff[SIZE];
int go[SIZE];
bool deg[SIZE];//度数,叶子节点度数为1,可以很好的区分 
int n,k,tot=0;
queue<int>q;
int ans=0;
void add(int x,int y)
{
    ver[++tot]=y,Next[tot]=head[x],head[x]=tot;
}
void bfs()
{
    q.push(go[1]),d[go[1]]=1;//?
    while(q.size())
    {
        int x=q.front();q.pop();
        int i;
        for(i=head[x];i;i=Next[i])
        {
            int y=ver[i];
            if(d[y])continue;
            d[y]=d[x]+1;
            f[y][0]=x;
            int j;
            for(j=1;j<=lg[d[x]];j++)
            {
                f[y][j]=f[f[y][j-1]][j-1];
            }
            q.push(y);
        }
    }
}
int lca(int x,int y)
{
    if(d[x]<d[y])swap(x,y);
    while(d[x]>d[y])
    {
        x=f[x][lg[d[x]-d[y]]-1];
    }
    if(x==y)return x;
    int i;
    for(i=lg[d[x]]-1;i>=0;i--)
    {
        if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
    }
    return f[x][0];
}
void dfs(int x,int pre)//统计一遍树上前缀和 
{
    int i;
    for(i=head[x];i;i=Next[i])
    {
        int y=ver[i];
        if(y==pre)continue;
        dfs(y,x);//此时y已经被更新过了 
        diff[x]+=diff[y]; 
    }
}
int main()
{
    cin>>n;
    int i;
    memset(deg,0,sizeof(deg));
    for(i=1;i<=n;i++)lg[i]=lg[i-1]+(1<<lg[i-1]==i);
    for(i=1;i<=n;i++)scanf("%d",&go[i]);
    memset(diff,0,sizeof(diff));
    for(i=1;i<=n-1;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        add(x,y);//反向边 
        add(y,x);
        deg[x]++,deg[y]++;
    }
    bfs();
    for(i=1;i<=n-1;i++)
    {
        int x=go[i],y=go[i+1]; 
        int anc=lca(x,y);
        diff[x]++,diff[f[y][0]]++,diff[anc]--,diff[f[anc][0]]--;//注意是y的前一个节点 
    }
    dfs(go[1],0);
    for(i=1;i<=n;i++)cout<<diff[i]<<endl;
    return 0;
}
原文地址:https://www.cnblogs.com/lipoicyclic/p/12658960.html