zoukankan      html  css  js  c++  java
  • bzoj3631 [JLOI2014]松鼠的新家

    Description

    松鼠的新家是一棵树,前几天刚刚装修了新家,新家有n个房间,并且有n-1根树枝连接,每个房间都可以相互到达,且俩个房间之间的路线都是唯一的。天哪,他居然真的住在“树”上。松鼠想邀请小熊维尼前来参观,并且还指定一份参观指南,他希望维尼能够按照他的指南顺序,先去a1,再去a2,……,最后到an,去参观新家。
    可是这样会导致维尼重复走很多房间,懒惰的维尼不听地推辞。可是松鼠告诉他,每走到一个房间,他就可以从房间拿一块糖果吃。维尼是个馋家伙,立马就答应了。
    现在松鼠希望知道为了保证维尼有糖果吃,他需要在每一个房间各放至少多少个糖果。因为松鼠参观指南上的最后一个房间an是餐厅,餐厅里他准备了丰盛的大餐,所以当维尼在参观的最后到达餐厅时就不需要再拿糖果吃了。

    Input

    第一行一个整数n,表示房间个数
    第二行n个整数,依次描述a1-an
    接下来n-1行,每行两个整数x,y,表示标号x和y的两个房间之间有树枝相连。

    Output

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

    Sample Input

    5
    1 4 5 3 2
    1 2
    2 4
    2 3
    4 5

    Sample Output

    1
    2
    1
    2
    1

    HINT

    2<= n <=300000

    【解析】

    上网上搜题解 说是一道裸题。。。然而我并没有看出哪里裸orz。(我很纯洁QWQ)

    昨晚徐老大给我讲了查分和前缀和。

    写一下....

    ===============================如果你知道差分就不用看下面我bb了。====================================================

    随便写一个序列 3 6 2 7 5 

    它的查分序列是用后一个数减去前一个数,第一个数什么都不减就是它本身。

    差分序列 3  3  -4  5  -2

    差分序列有一个性质,就是你从头往后累加,加到第几个位置,现在累加的数就是原序列这个位置的数。

    例如:差分序列加到第三个位置 3+3+(-4)=2;正好是原序列第三个位置的数。

    应用一下吧。。。。我们要对一个区间的子区间进行操作.

    例如[1,10];我们要将[4,6]这个区间的所有数加+1,求[4,6]的区间和。当然可以for循环挨个加1,求和;

    那么差分是怎样求呢。我们只需更改两个位置,假如我们进行修改的区间是[l,r]。将a[l]+1,a[r+1]-1;即可完成区间的更改。

    上面的例子 [4,6] .a[4]+1,a[6+1]-1;这样从头开始累加,由于我们在a[4]+1,我们从头累加到a[4-6]都会加+1;当我们累加到[4,6]

    这个区间以外,由于a[6+1]已经-1,所以之后的累加都-1,都是它原来数的大小。为什么累加就是利用的差分序列的性质。

    ==============================orz===分界线===zro==========================================================

    咳咳 进入正题:

    题目大意:就是求每个房间被访问的次数。将每条路径上的结点的权值+1,最后求每个结点的值。

    然而我还没有看出这是一道裸体。(哦不 裸题。=.=)

    ================================下面的结论挺好==============================================================

    我上网上搜了一下差分的性质,,我就是想看看怎么用差分。

    好:树上差分的结论:

     1、将每条路径(s,t)上的每个点权值增加1,求各点权值。

    将s、t点的权值加+1,lca(s,t)的权值-1,s和t的lca的爸爸-1。从叶子结点开始向上累加权值。

    恍然大悟WAW。这道题真是赤裸裸的一道裸题啊。至于为什么那个点+那个点-,结合差分序列的性质自己顿悟。

    2、找出被所有路径都覆盖的边

    s,t的权值+1,s和t的lca的权值-2,从叶结点向上累加。

    最终权值为路径数的点到其父亲的边为所求边。

    ========================================================================================================

    所以我们把他访问的路径看成一个区间,区间的结点的权值都+1,就用差分做好了。

    但是有一个坑,这一条路径的起点是这一条路径的终点,所以不能重复给糖,最后2--n减去1就可以。

    =========================================================================================================

    终于到贴代码的时间了。QWQ。

    【代码】

    #include<iostream>
    #include<cstdio>
    #include<vector>
    using namespace std;
    #define N 300009
    int n,x,y;
    int a[N],v[N],deep[N],dad[N][22];
    vector<int>vec[N];
    inline int read()
    {
        int x=0,f=1;char ch=getchar();
        while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
        while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
        return f*x;
    }
    inline void dfs(int x)
    {
        deep[x]=deep[dad[x][0]]+1;
        for(int i=0;dad[x][i];i++)
        dad[x][i+1]=dad[dad[x][i]][i];
        for(int i=0;i<vec[x].size();i++)
        {
            if(!deep[vec[x][i]])
            {
                dad[vec[x][i]][0]=x;
                dfs(vec[x][i]);
            }
        }
    }
    inline int lca(int x,int y)
    {
        if(deep[x]>deep[y])swap(x,y);
        for(int i=20;i>=0;i--){if(deep[dad[y][i]]>=deep[x])y=dad[y][i];}
        if(x==y)return x;
        for(int i=20;i>=0;i--)if(deep[dad[x][i]]!=deep[dad[y][i]])x=dad[x][i],y=dad[y][i];
        return dad[x][0];
    }
    inline void dfs1(int x)
    {
        for(int i=0;i<vec[x].size();i++)
        {
            if(dad[x][0]!=vec[x][i])
            dfs1(vec[x][i]),v[x]+=v[vec[x][i]];//从叶子结点向上累加权值 
        }
    }
    int main()
    {
         n=read();
         for(int i=1;i<=n;i++)
         a[i]=read();
         for(int i=1;i<n;i++)
         {
             x=read();y=read();
             vec[x].push_back(y);
             vec[y].push_back(x);
         }
         dfs(1);
         for(int i=1;i<n;i++)//差分 
         {
             v[a[i]]++;v[a[i+1]]++;
             v[lca(a[i],a[i+1])]--;
             v[dad[lca(a[i],a[i+1])][0]]--;
         }
         dfs1(1);
         for(int i=2;i<=n;i++)
         v[a[i]]--;//减去重复的。 
         for(int i=1;i<=n;i++)
         {
             printf("%d
    ",v[i]);
         }
         return 0;
    } 

     然而上一个代码是超时的orz。

    因为树剖的复杂度是logn,tarjan n+q,理论上是tarjan最优

    因为tarjan要跑完整个图,所以他的实际复杂度比理论复杂度高。而树剖可能跑不完整个图就找到答案,这时树剖就比tarjan优秀了。

    如果要写tarjan,写上路径压缩和启发式合并(然而我并不知道启发式合并是什么鬼)。

    下面是树剖的code

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<vector>
    #define MAXN 300000+15
    using namespace std;
    int n,x,y,ans[MAXN],a[MAXN];
    vector<int>vec[MAXN];
    int size[MAXN],dad[MAXN],deep[MAXN],top[MAXN];
    inline int read(){
        int x=0,f=1;
        char ch=getchar();
        while(ch<'0'||ch>'9'){
            if(ch=='-')f=-1;
            ch=getchar();
        }
        while(ch>='0'&&ch<='9'){
            x=x*10+ch-'0';
            ch=getchar();
        }
        return f*x;
    }
    inline void dfs(int x){
        deep[x]=deep[dad[x]]+1;
        size[x]=1;
        for(int i=0;i<vec[x].size();i++)
            if(vec[x][i]!=dad[x]){
                dad[vec[x][i]]=x;
                dfs(vec[x][i]);
                size[x]+=size[vec[x][i]];
            }
    }
    inline void dfs1(int x){
        int t=0;
        if(!top[x])    top[x]=x;
        for(int i=0;i<vec[x].size();i++)
            if(dad[x]!=vec[x][i]&&size[vec[x][i]]>size[t])
                t=vec[x][i];
        if(t){
            top[t]=top[x];
            dfs1(t);
        }    
        for(int i=0;i<vec[x].size();i++)
            if(dad[x]!=vec[x][i]&&vec[x][i]!=t)
                dfs1(vec[x][i]);
    }
    inline int LCA(int x,int y){
        for(;top[x]!=top[y];){
            if(deep[top[x]]<deep[top[y]])
                swap(x,y);
            x=dad[top[x]];
        }
        if(deep[x]>deep[y])
            swap(x,y);
        return x;
    }
    inline void dfs2(int x){
        for(int i=0;i<vec[x].size();i++)
            if(dad[x]!=vec[x][i]){
                dfs2(vec[x][i]);
                ans[x]+=ans[vec[x][i]];
            }
    }
    int main(){
        n=read();
        for(int i=1;i<=n;i++)
            a[i]=read();
        for(int i=1;i<n;i++){
            x=read();
            y=read();
            vec[x].push_back(y);
            vec[y].push_back(x);
        }
        deep[1]=1;
        dfs(1);
        dfs1(1);
        for(int i=2;i<=n;i++){
            int x=a[i-1],y=a[i],z=LCA(x,y);
            ans[x]++;ans[dad[y]]++;
            ans[z]--;ans[dad[z]]--;
        }
        dfs2(1);
        for(int i=1;i<=n;i++)    cout<<ans[i]<<endl;
    }
  • 相关阅读:
    hdu 2222 Keywords Search
    Meet and Greet
    hdu 4673
    hdu 4768
    hdu 4747 Mex
    uva 1513 Movie collection
    uva 12299 RMQ with Shifts
    uva 11732 strcmp() Anyone?
    uva 1401
    hdu 1251 统计难题
  • 原文地址:https://www.cnblogs.com/zzyh/p/6816019.html
Copyright © 2011-2022 走看看