天天爱跑步

一道:树上差分+LCA+桶的题

说实话,这道题放在D1T2就是非常不合理的。然而CCF就是放了,并且还是能依靠CSP捞钱,你也必须交钱参加比赛。这个社会是多么的不公啊!闲扯结束

显然如果对每条路径都进行一次处理,复杂度不对。考虑对路径进行一次预处理,然后进行统一的计算答案。我们发现当一条路径对某一个点P产生贡献时满足这个条件:

1.当\(P\)在该路径的\(S\)\(LCA\)之间时 \(dep[S]-dep[P]=watch[P]\) 移一下项为\(dep[S]=dep[P]+watch[P]\)那么该路径起点对P有贡献

2.当\(P\)在该路径的\(LCA\)\(T\)之间时 \(dist[S,T]-watch[P]=dep[T]-dep[P]\) 移一下项\(dist[S,T]-dep[T]=watch[P]-dep[P]\)那么该路径终点对P有贡献

然后用一个全局桶维护这些贡献 当枚举到当前点的时候 将当前点作为终点和起点的贡献加在桶里面。然后计算当前点的ans

细节:一个它只会受其子树的贡献,其他的贡献不会有的。如何处理?在开始遍历这个点的时候用\(ad1,ad2\)来存储已经存在的桶里面的值 然后遍历子树 当又返回到当前节点的时候 计算当前节点的答案。显然当前节点的答案为\(ans[x]+=bas1[watch[x]+dep[x]]-ad1+bas2[watch[x]-dep[x]+maxn]-ad2\)

注意到我们对\(bas2\)的计算的时候加了一个\(maxn\) ,其实原因很显然 \(watch[x]-dep[x]\)有可能小于零

还有就是当遍历完当前点 应该将以当前点为LCA的路径的影响全部消除。因为如果以当前点为LCA,显然不会对当前点的父亲及祖先产生影响,应该减掉

然后就是代码了QAQ 我是用的邻接表存储的以当前节点为终点的路径编号,以及以当前节点为LCA的路径编号

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#define int long long 
using namespace std;
const int maxn=500010;
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 x*f;
}
int n,m,fir[maxn],nxt[maxn<<1],to[maxn<<1],tot=1,watch[maxn],fa[maxn][25];
int st[maxn],ed[maxn],stt[maxn],dist[maxn],ans[maxn],dep[maxn];
int fir1[maxn],nxt1[maxn<<1],to1[maxn<<1],tot1,fir2[maxn],nxt2[maxn<<1],to2[maxn<<1],tot2;
void add(int x,int y){nxt[++tot]=fir[x];fir[x]=tot;to[tot]=y;}
void dfs1(int x){
	dep[x]=dep[fa[x][0]]+1;
	for(int i=0;i<=20;i++)
		fa[x][i+1]=fa[fa[x][i]][i];
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(y==fa[x][0]) continue;
		fa[y][0]=x;dfs1(y);
	}
}
int LCA(int x,int y){
	if(dep[x]<dep[y]) swap(x,y);
	for(int i=20;i>=0;i--){
		if(dep[fa[x][i]]>=dep[y]) x=fa[x][i];
		if(x==y) return x;
	}
	for(int i=20;i>=0;i--){
		if(fa[x][i]!=fa[y][i]){
			x=fa[x][i];y=fa[y][i];
		}
	}
	return fa[x][0];
}
void add1(int x,int y){nxt1[++tot1]=fir1[x];fir1[x]=tot1;to1[tot1]=y;}
void add2(int x,int y){nxt2[++tot2]=fir2[x];fir2[x]=tot2;to2[tot2]=y;}
int bas1[maxn<<1],bas2[maxn<<1],ad1,ad2;
void dfs2(int x){
	ad1=bas1[watch[x]+dep[x]];ad2=bas2[watch[x]-dep[x]+maxn];
	for(int i=fir[x];i;i=nxt[i]){
		int y=to[i];if(y==fa[x][0]) continue;
		dfs2(y);
	}
	bas1[dep[x]]+=stt[x];
	for(int i=fir1[x];i;i=nxt1[i]){
		int y=to1[i];
		bas2[dist[y]-dep[ed[y]]+maxn]++;
	}
	ans[x]+=bas1[watch[x]+dep[x]]-ad1+bas2[watch[x]-dep[x]+maxn]-ad2;
	for(int i=fir2[x];i;i=nxt2[i]){
		int y=to2[i];
		bas1[dep[st[y]]]--;
		bas2[dist[y]-dep[ed[y]]+maxn]--;
	}
}
signed main(){
	n=read();m=read();
	for(int i=1,x,y;i<=n-1;i++){
		x=read();y=read();
		add(x,y);add(y,x);
	}
	for(int i=1;i<=n;i++) watch[i]=read();
	dep[1]=1;
	dfs1(1);
	for(int i=1,x,y,lca;i<=m;i++){
		x=read();y=read();
		lca=LCA(x,y);st[i]=x;ed[i]=y;
		dist[i]=dep[x]+dep[y]-2*dep[lca];
		stt[x]++;add1(y,i);add2(lca,i);
		if(dep[lca]+watch[lca]==dep[x]) ans[lca]--;
	}
	dfs2(1);
	for(int i=1;i<=n;i++)
		printf("%lld ",ans[i]);
}
posted on 2019-11-04 21:48  萌德真帅  阅读(118)  评论(0编辑  收藏  举报