用的是一种很容易理解但写起来却有点小烦的差分。
思路
算法模型构建:
本题实际上求的是两种情况:
1.在i节点下方的Wi层有多少个 起点,
2.在i节点下方的有多少个满足一定条件的终点。
情况1:情况2:
现在考虑起点和终点的作用域:
接着我们考虑如何处理,如果是链状接构是可以直接差分的,在树上,我们同样也可以差分。
也就是在末节点的再上一个节点打上一个del标记。
而我们的答案就是此节点的子树的起点集群中:
dep[j]=dep[i]+W[i] 的j节点的个数
即cnt1[ dep[i]+W[i] ]
此节点中终点集群中:
dep[j]-dep[i]=Len-W[i]的节点j的个数
化简得: dep[j]-Len=dep[i]-W[i]
对于终点类型的数据,我们存储他的Dep[j]-Len
则答案为cnt2[ dep[i]-W[i] ]
最后再减去该子树中的del1和del2
如何处理子树信息:
将每个节点进入时的cnt1,cnt2,del1,del2信息记录
在扫描完他的子树后的cnt1,cnt2,del1,del2相减
即可得到子树上的cnt1,cnt2,del1,del2
但因为一个节点只会关心cnt1,del1的dep[i]+W[i]
和cnt2,del2的Dep[i]-W[i]
所以只要维护这些值的信息就行了。
嘟嘟噜,提到现在已经基本上做完了,最后一个问题,如何存储那些信息,将每个起点、终点、LCA的信息放到每个节点是不现实的(空间要开n^2),所以我们可以先将他们全部存储下来,再按照他们离开dfs搜索栈时的时间戳排序,之后用while把排完序的数组的尾部依次取出即可。
Code
#include<bits/stdc++.h>
#define FOR(i,a,b) for(int i=a;i<=b;++i)
#define LL long long
#define Maxn 300000
#define Ban 300000
#define IL inline
using namespace std;
int read(){
char c=getchar(); int flag=1, x=0;
while(!isdigit(c)){ if(c=='-') flag=-1; c=getchar(); }
while(isdigit(c) ){ x=x*10+(c-'0'); c=getchar(); }
return flag*x;
}
int n,m;
struct Graph_Type{
struct LINE{
int st,en,pre;
}line[Maxn*2+10];
int head[Maxn+10], tot=0;
IL void add(int a,int b){
line[++tot]=(LINE){ a,b,head[a] }; head[a]=tot;
line[++tot]=(LINE){ b,a,head[b] }; head[b]=tot;
}
int Fa[Maxn+10][30], dep[Maxn+10]; int dfn[Maxn+10], cnt=0;
int ord[Maxn+10],cnt2=0;
IL void dfs1(int now,int F){
Fa[now][0]=F; dfn[now]=++cnt; dep[now]=dep[F]+1;
FOR(i,1,20) Fa[now][i]=Fa[ Fa[now][i-1] ][i-1];
for(int i=head[now];i;i=line[i].pre){
int E=line[i].en;
if(E==F) continue;
dfs1(E,now);
}
ord[now]=++cnt2;
}
IL int LCA(int x,int y){
if(dfn[x]<dfn[y]) swap(x,y);
for(int i=20;i>=0;i--)
if(dfn[Fa[x][i]]>dfn[y])
x=Fa[x][i];
return Fa[x][0];
}
}S;
int T[Maxn+10];
int data[Maxn+10];
struct Chg_Type{
int id,data;
}add1[Maxn+10],add2[Maxn+10],del1[Maxn+10],del2[Maxn+10];
int cnt;
bool cmp(Chg_Type a,Chg_Type b){
return S.ord[a.id]<S.ord[b.id];
}
int Ans[Maxn+10];
int A1[Maxn+10],A2[2*Maxn+10],D1[Maxn+10],D2[2*Maxn+10];
int nA1,nA2,nD1,nD2;
IL void dfs(int now){
//cout<<" ^ "<<now<<" "<<S.dfn[now]<<endl;
int data1=S.dep[now]+T[now]; int data2=S.dep[now]-T[now]+Ban;
int tA1=A1[data1], tA2=A2[data2], tD1=D1[data1], tD2=D2[data2];
for(int i=S.head[now];i;i=S.line[i].pre){
int E=S.line[i].en;
if(E==S.Fa[now][0]) continue;
dfs(E);
}
while( nA1<=cnt && add1[nA1].id==now )
A1[add1[nA1].data]++, nA1++;
while( nA2<=cnt && add2[nA2].id==now )
A2[add2[nA2].data]++, nA2++;
while( nD1<=cnt && del1[nD1].id==now )
D1[del1[nD1].data]++, nD1++;
while( nD2<=cnt && del2[nD2].id==now )
D2[del2[nD2].data]++, nD2++;
Ans[now] = ( A1[data1]-tA1 )+( A2[data2]-tA2 )-( D1[data1]-tD1 )-( D2[data2]-tD2 );
return;
}
int main(){
n=read(); m=read();
FOR(i,1,n-1){
int a=read(), b=read();
S.add(a,b);
}
S.dfs1(1,0);
FOR(i,1,n) T[i]=read();
FOR(i,1,m){
int a=read(); int b=read(); int L=S.LCA(a,b);
int Len=S.dep[a]+S.dep[b]-2*S.dep[L];
++cnt;
add1[cnt].id=a; add1[cnt].data=S.dep[a];
add2[cnt].id=b; add2[cnt].data=S.dep[b]-Len+Ban;
del1[cnt].id=S.Fa[L][0]; del1[cnt].data=S.dep[a];
del2[cnt].id=L; del2[cnt].data=S.dep[b]-Len+Ban;
}
sort(add1+1,add1+cnt+1,cmp);
sort(add2+1,add2+cnt+1,cmp);
sort(del1+1,del1+cnt+1,cmp);
sort(del2+1,del2+cnt+1,cmp);
nA1=1; nA2=1; nD1=1; nD2=1;
S.ord[0]=n+10;
dfs(1);
FOR(i,1,n) cout<<Ans[i]<<" ";
return 0;
}
```