Description
Input
Output
Sample Input
1 4 5 3 2
1 2
2 4
2 3
4 5
Sample Output
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; }