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\n",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;
}

 

posted @ 2017-05-06 10:50  ANhour  阅读(186)  评论(0编辑  收藏  举报