【NOIP2016】DAY1 T2 天天爱跑步
Description
Input
Output
输出1行N 个整数,第个整数表示结点的观察员可以观察到多少人。
Sample Input
2 3
1 2
1 4
4 5
4 6
0 2 5 1 2 3
1 5
1 3
2 6
Sample Output
HINT
Solution
这道题目吧,好难……
首先,我们读入的是一棵树,在某些结点上有一些玩家(废话,读过题的都知道)
那么对于某一个玩家, 其实在树的某条链上有两种情况,一种是从下向上,另一种是从上向下,即该玩家还没走到起点和终点的lca,另一种已经过了起点和终点的lca(我废话真多)
我们先对于其中一种情况讨论,当该点从下向上时(我不会说是因为这种情况比较简单),如图P1,我们可以看出当且仅当w[i]+d[i]=d[x]时,从x出发的玩家才能被在i点的观察员观察到(d[i]表示结点i的深度),那么当该点从上向下时呢?我们看P2,显然当且仅当d[y]-d[i]=dis(x,y)-w[i](dis(x,y)表示x与y之间的距离)我们把这个式子变形一下,w[i]-d[i]=dis(x,y)-d[y](当然这个式子也可以写成w[i]-d[i]=d[x]-2*d[lca(x,y)],因为dis(x,y)=d[x]+d[y]-2*d[lca(x,y)])
P1 P2
现在我们遇上了一个大问题,这两个式子如何来实现?
利用一个高档货,树上差分(不知道哪里高档,反正就是高档就对了),我们便可以在x上+1,在lca(x,y)上-1,对于y也相同,在y上+1,在lca(x,y)上-1,当我们对于这棵树dfs的时候,遍历到i时,利用差分就可以做到求出在i的子树中的能走到i的玩家数
可是我们要求的不仅仅是玩家经过该点,还需在同一时刻出现,其实我们只要做两个桶就可以了,桶p[k]记录起点的d[x]=k的玩家数量,桶q[k]记录终点的dis(x,y)-d[y]=k的玩家数量,对于每个点的答案就是q[d[i]+w[i]]+p[w[i]-d[i]]-这个点遍历前q[d[i]+w[i]]+p[w[i]-d[i]](因为有些玩家从该点的祖先开始或结束但还没走到lca),当然我们需要记录以某个结点开始的玩家和以某个结束的玩家(这句话别看,因为我看了也懵,看下下面程序再结合一下这句话大概应该可能就可以看懂了吧)
还有一个小问题,就是当w[lca(x,y)]+d[lca(x,y)]==d[x]时,我们会发现向上走和向下走都会对lca有贡献,所以当lca可以观测到时我们需要把lca处的答案-1
但是还有一个坑,就是w[i]-d[i]可能小于0,那就把桶里面的数都加300000就可以了
讲得好水,看下程序吧(虽然说很丑)
1 #include<cstdio> 2 #include<utility> 3 struct r{ 4 int to,last,w; 5 }e[600002],in1[300002],ou1[300002],in2[300002],ou2[300002]; 6 int num=1,num1=1,num2=1,num3=1,num4=1,head[300002],head1[300002],head2[300002], 7 head3[300002],head4[300002],w[300002],d[300002],f[300002][23],ans[300002]; 8 void add(int u,int v){ 9 e[num].to=v; 10 e[num].last=head[u]; 11 head[u]=num++; 12 } 13 void add_edge(int u,r &p,int*q,int&num,int w){ 14 p.w=w; 15 p.last=q[u]; 16 q[u]=num++; 17 } 18 inline int readin(){ 19 int c=getchar(),ret=0; 20 while (c<'0'||c>'9') c=getchar(); 21 while (c>='0'&&c<='9') ret=(ret<<3)+(ret<<1)+c-'0',c=getchar(); 22 return ret; 23 } 24 void dfs(int k,int fa){ 25 d[k]=d[fa]+1; 26 f[k][0]=fa; 27 for (int i=1;i<=20;i++) f[k][i]=f[f[k][i-1]][i-1]; 28 for (int i=head[k];i;i=e[i].last)if (e[i].to!=fa){ 29 dfs(e[i].to,k); 30 } 31 } 32 int lca(int u,int v){ 33 if (d[u]>d[v]) std::swap(u,v); 34 int y=d[v]-d[u]; 35 for (int i=20;i>=0;i--) if ((y&(1<<i))) v=f[v][i]; 36 if (u==v) return u; 37 for (int i=20;i>=0;i--) if (f[u][i]!=f[v][i]) u=f[u][i],v=f[v][i]; 38 return f[u][0]; 39 } 40 int q[600005],p[600005]; 41 void dfs2(int k,int fa){ 42 int req=q[d[k]+w[k]],rep=p[w[k]-d[k]+300000]; 43 for (int i=head1[k];i;i=in1[i].last) q[in1[i].w]++; 44 for (int i=head3[k];i;i=in2[i].last) p[in2[i].w]++; 45 for (int i=head[k];i;i=e[i].last) if (e[i].to!=fa) dfs2(e[i].to,k); 46 ans[k]+=q[d[k]+w[k]]-req+p[w[k]-d[k]+300000]-rep; 47 for (int i=head2[k];i;i=ou1[i].last) q[ou1[i].w]--; 48 for (int i=head4[k];i;i=ou2[i].last) p[ou2[i].w]--; 49 } 50 int main() 51 { 52 int n=readin(),m=readin(),u,v,x,y; 53 for (int i=1;i<n;i++) u=readin(),v=readin(),add(u,v),add(v,u); 54 num=0; 55 dfs(1,0); 56 for (int i=1;i<=n;i++) w[i]=readin(); 57 for (int i=1;i<=m;i++){ 58 x=readin(),y=readin(); 59 int z=lca(x,y); 60 if (d[x]==d[z]+w[z]) ans[z]--; 61 add_edge(x,in1[num1],head1,num1,d[x]), 62 add_edge(z,ou1[num2],head2,num2,d[x]), 63 add_edge(y,in2[num3],head3,num3,d[x]-d[z]*2+300000), 64 add_edge(z,ou2[num4],head4,num4,d[x]-d[z]*2+300000); 65 } 66 dfs2(1,0); 67 for (int i=1;i<=n;i++) 68 printf("%d ",ans[i]); 69 return 0; 70 }