noip 2016 天天爱跑步(倍增+差分)

一个人从a跑到b。

l=lca(a,b)//倍增

f[l][0]:为l的爸爸。

把他拆成四个人。

分别从a跑到根的人。

从f[l][0]跑到根的人。

从根跑到b的人。

从根跑到l的人。//差分的思想,

我们要让a到b最短路径上所有的点经过次数都+1,并记录时间

那么在差分数组上让a点+1,b点+1,l点-1,,fa[l][0](l的父亲)-1;

如何记录每一个人从a跑到b的信息?

一个点a记四个信息(a,t,d,p):

a:起点(如果是从a出发)/终点(如果是从根出发)为a;

t:表示从a出发时间

d:当这个人跑过时经过次数+1还是-1;

p:是从a出发到根(0)还是从根出发到a(1)

我们对于一个人记录4个点的信息这四个点分别为a,b,l,fa[l][0](l的父亲),(记录用邻接链表)

add2(a,t,d,p)//加入一条信息,表示如果p == 0,有一个人在t从a点出发,他要走到根,他所经过的点的次数应该加d;

                                               表示如果p == 1,有一个人在t从根出发,他要走到a,他所经过的点的次数应该加d;

add2(a,0,1,0);
add2(fa[l][0],dep[a]-dep[l]+1,-1,0);//从fa[l][0]为到l后的后一秒出发时间为dep之差
add2(b,dep[a]-dep[l]*2,1,1);//在没拆之前这个人要在dis(a,b) = dep[a]+dep[b]-dep[l]-dep[l] 时跑到b,那么他从根出发时间为dep[a]-dep[l] -dep[l];他要花dep[b]时间从根跑到b.

add2(l,dep[a]-dep[l]*2,-1,1);//没拆之前这个人要在dep[a]-dep[l]的时间到l,那么拆了后他从根出发的时间则为dep[a]-dep[l]*2

如果检查员在i点,jcy[i]表示检查员在i点的时间

如果人是从a出发到根,dep[a]-dep[i]+t=jcy[i],dep[a]+t=jcy[i]+dep[i]//t是从a出发的时间

从人是根出发到a,           t+dep[i]=jcy[i],t=jcy[i]-dep[i]

(只要满足这个等式就可以被观察到,即i观察到的人数+1)

jcy[i]-dep[i]可能小于0,所以所有等式两边+n(点的总数)

因为a肯定在i的子树里,所以求可以被在jcy[a]时被站在a的检查员观察到的人数时是不会影响在jcy[int]时被站在i点的检查员观察到的人数的。

引进tong1[i](表示从a出发到根,被在i点的检查员观察到的人数)

tong2[i](表示从根出发到a,被在i点的检查员观察到的人数)

#include<iostream>
#include<cstdio>
using namespace std;
int ans[300010],n,m,jcy[300010];
int tov[600010],nex[600010],h[300010],tp;
int tpp,nexx[1200010],tod[1200010],top[1200010],tot[1200010],hh[300010];
int tong1[1000010],tong2[1000010];
int dep[300010]={0};  
int fa[300010][20]={0};  
bool vis[300010]={0};  
void add(int x,int y)
{
	tp++;
	nex[tp]=h[x];
	tov[tp]=y;
	h[x]=tp;
}
void add2(int qi,int t,int d,int p)
{
	tpp++;
	nexx[tpp]=hh[qi];
	tod[tpp]=d;
	top[tpp]=p;
	tot[tpp]=t;
	hh[qi]=tpp;
	
}
void dfs(int v)  
{  
  vis[v]=1;  
  for(int i=h[v];i;i=nex[i])  
    if (!vis[tov[i]])  
    {  
      dep[tov[i]]=dep[v]+1;  
      fa[tov[i]][0]=v;  
      dfs(tov[i]);  
    }  
}  
  
int lca(int x,int y)  
{  
  if (dep[x]<dep[y]) swap(x,y);  
  for(int i=19;i>=0;i--)  
    if (dep[fa[x][i]]>=dep[y]) x=fa[x][i];  
  if (x==y) return x;  
  for(int i=19;i>=0;i--)  
    if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];  
  return fa[x][0];  
}  
void solve(int x)
{
   int x1=jcy[x]+dep[x]+n,x2=jcy[x]-dep[x]+n;
   int val1=tong1[x1],val2=tong2[x2];
   for(int i=hh[x];i;i=nexx[i])
   {
   	if(top[i]==0)tong1[tot[i]+dep[x]+n]+=tod[i];
   	else tong2[tot[i]+n]+=tod[i];
   }
   for(int i=h[x];i;i=nex[i])
   {
   	int v=tov[i];
   	if(dep[v]>dep[x])solve(v);
   	}
   	ans[x]=tong1[x1]+tong2[x2]-val1-val2;
}
int main()
{
	//freopen("running.in","r",stdin);
	//freopen("running.out","w",stdout);
	cin>>n>>m;
	for(int i=1;i<n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		add(x,y);
		add(y,x);
	}
	dep[1]=0;dep[0]=-1;  
  dfs(1);  
  for(int i=1;i<=19;i++)  
    for(int j=1;j<=n;j++)  
      fa[j][i]=fa[fa[j][i-1]][i-1];  
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&jcy[i]);
	}
	for(int i=1;i<=m;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		int l=lca(a,b);
		add2(a,0,1,0);
		add2(fa[l][0],dep[a]-dep[l]+1,-1,0);
		add2(b,dep[a]-dep[l]*2,1,1);
		add2(l,dep[a]-dep[l]*2,-1,1);
	}
	solve(1);
	for(int i=1;i<=n;i++)
	{
		printf("%d ",ans[i]);
	}
	return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值