一道糾結了很久的題
重點不知道的是差分是對於每個桶而言的
樹上差分能解決一部分樹上統計問題
u表示起點,v表示終點
對於一條u到v的路徑,先討論LCA!=u&&LCA!=v的情況:
分爲u到LCA的路徑和LCA到v的路徑
對於u到LCA的路徑上的點x,當deep[u]-deep[x]=w[x]時,即w[x]+deep[x]=deep[u]時,這條路徑對點x有貢獻;
觀察發現w[x]+deep[x]是定值,所以統計經過x的路徑中,deep[u]=w[x]+deep[x]的路徑條數。
對於LCA到v的路徑上的點x,當deep[u]-2*deep[LCA]+deep[x]=w[x]時,即w[x]-deep[x]=deep[u]-2*deep[lca]時,這條路徑對點x有貢獻;
觀察發現w[x]-deep[x]是定值,所以統計經過x的路徑中,deep[u]-2*deep[lca]=w[x]-deep[x]的路徑條數;
接下來就是統計路徑條數了,用到樹上差分
我們統計的起點(終點)一定在點x子樹內,所以統計x子樹內有多少起點(終點)的值等於所需值
即統計有多少個在點x子樹內的起點的deep[u]的值與deep[x]+w[x]相同
有多少終點的deep[u]-2*deep[lca]與w[x]-deep[x]相同
對於一個值,再u、v上加一個表示這個值+1的標記
考慮到x子樹內的路徑不一定經過x,所以在father[LCA]上加一個標記表示這個值-1
標記用動態數組儲存 然後一遍dfs用兩個桶分別統計,統計時值統一加上n,因爲可能出現負數
記錄下dfs到父親節點時自己(也就是父親的兒子)所需值的個數,然後統計完子樹的值之後再做差計算自己
對於LCA==u||LCA==v的情況歸於以上兩類計算,特殊處理一下
另外,對於分裂成兩條鏈LCA可能會被統計兩遍,最後特殊判斷一下,如果被統計了兩遍就減去一遍,
複雜度:LCA O(mlogn)dfs統計 O(n)
#include<bits/stdc++.h>
using namespace std;
const int N=350000;
int Max,x,y,z,len,n,m,tot,w[N],tong1[4*N],tong2[4*N],ans[N],to[N*2],head[N],next1[N*2],d[N],f[N][30];
struct p{
int type,zhi,qz;
};
int read(){
char ch=getchar();int x=0,op=1;
for (;!isdigit(ch);ch=getchar()) if (ch=='-') op=-1;
for (;isdigit(ch);ch=getchar()) x=(x<<1)+(x<<3)+ch-'0';
return x*op;
}
vector <p> t[4*N];
void add(int x,int y)
{ next1[++tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void dfs(int x)
{ for (int i=head[x];i;i=next1[i])
{ int v=to[i];
if (!d[v])
{ d[v]=d[x]+1;
f[v][0]=x;
dfs(v);}
}
}
int lca(int x,int y)
{ if (d[x]<d[y]) swap(x,y);
int tmp=d[x]-d[y];
for (int i=25;i>=0;i--)
if ((tmp>>i)&1)
{x=f[x][i];}
if (x==y) { return x;}
else {for (int i=25;i>=0;i--)
if (f[x][i]!=f[y][i]) { x=f[x][i]; y=f[y][i];}
x=f[x][0]; return x;
}
}
void solve(int x)
{ int tmp=tong1[d[x]+w[x]]+tong2[d[x]-w[x]+Max];//tong1記錄向上走的鏈 tong2記錄向下走的鏈
for (int i=0;i<t[x].size();i++)
{ if (t[x][i].type==1) {tong1[t[x][i].zhi]+=t[x][i].qz;}
else {tong2[t[x][i].zhi]+=t[x][i].qz;}}
for (int i=head[x];i;i=next1[i])
{ if (d[to[i]]>d[x]) { solve(to[i]);} }
ans[x]=tong1[d[x]+w[x]]+tong2[d[x]-w[x]+Max]-tmp;
}
int main()
{ n=read(); m=read(); Max=3*n;
for (int i=1;i<=n-1;i++)
{ x=read(); y=read(); add(x,y); add(y,x);}
d[1]=1;dfs(1);
for (int i=1;i<=n;i++)
for (int j=1;(1<<j)<d[i];j++)
{ f[i][j]=f[f[i][j-1]][j-1];}
for (int i=1;i<=n;i++) w[i]=read();
for (int i=1;i<=m;i++)
{ x=read(); y=read(); z=lca(x,y);
len=d[x]+d[y]-2*d[z];
t[x].push_back(p{1,d[x],1});
t[y].push_back(p{2,d[y]-len+Max,1});
t[z].push_back(p{1,d[x],-1});
t[f[z][0]].push_back(p{2,d[y]-len+Max,-1});//對桶進行樹上差分
}
solve(1);
for (int i=1;i<=n-1;i++) cout<<ans[i]<<' ';
cout<<ans[n]<<endl;
}