• 洛谷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;
    }
  • 相关阅读:
    使用Visual Studio .Net 做自己的汉化软件
    给所有的Control加两个属性,实现回车键自动跳转到下一个控件
    数字逗号标记—以前原创(一)
    解决w3wp.exe占用CPU和内存问题
    sql日期函数
    索引的使用总结
    w3wp.exe狂占内存
    w3wp.exe占内存CPU问题 WIN2003 IIS6.0假死现象的分析
    查看Linux系统日志
    linux动态增加LV空间
  • 原文地址:https://www.cnblogs.com/lipoicyclic/p/12658960.html
Copyright © 2020-2023  润新知