我们把每一条路径拆成$u->lca$和$lca->v$的路径
先考虑$u->lca$,如果这条路径会对路径上的某一个点产生贡献,那么满足$dep[u]-dep[x]=w[x],dep[u]=dep[x]+w[x]$,注意到$dep[x]+w[x]$是一个定值,所以我们只要去找它的子树里有多少个点的$dep$等于$dep[x]+w[x]$就可以了,这个可以直接开一个桶。然而如果点$x$在$lca$的上面,这一条路径是不会对他产生贡献的,那么我们就得在$lca$处把这一条路径的贡献给减去。具体怎么做呢?我们可以利用树上差分的思想,在点$u$把它的出现次数$+1$,在$lca$处把它的出现次数$-1$,那么我们对于每一个点,只要去查询它的子树里$dep[x]+w[x]$这个值出现了多少次就可以了。
顺便注意一下,因为我们的桶里存的不止是一棵子树的答案,所以查询得到的次数可能是来子其他子树的。那么我们可以dfs进去之前先记录一下,完了之后再记录一下,两次的差就是这个值在其子树里的实际出现次数
然后$lca->v$的路径咋搞嘞?只要把式子改成$dep[v]-dep[x]=len-w[i]$($len$表示路径长度),然后和上面一样的做法。注意这里有可能会出现负数,所以我们要让它加上$3e5$
注意到$lca$会被我们统计两次,那么我们只要最后做完之后,把所有被统计了两次答案的$lca$答案减一就好了
1 //minamoto 2 #include3 #include 4 #include 5 #include 6 using namespace std; 7 #define getc() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++) 8 char buf[1<<21],*p1=buf,*p2=buf; 9 template inline bool cmax(T&a,const T&b){ return a 1<<20)Ot();if(x<0)sr[++C]=45,x=-x; 24 while(z[++Z]=x%10+48,x/=10); 25 while(sr[++C]=z[Z],--Z);sr[++C]=' '; 26 } 27 const int N=300005,M=600005; 28 int head[N],Next[M],ver[M],tot; 29 int son[N],sz[N],fa[N],dep[N],top[N]; 30 int c[N],w[N],val[N],num[1000005]; 31 int n,m,lim,ans[N]; 32 vector qaq[N],qaq2[N],qaq3[N]; 33 struct node{ int s,t,lca,len;}q[N]; 34 inline void add(int u,int v){ 35 ver[++tot]=v,Next[tot]=head[u],head[u]=tot; 36 ver[++tot]=u,Next[tot]=head[v],head[v]=tot; 37 } 38 void dfs1(int u){ 39 sz[u]=1,dep[u]=dep[fa[u]]+1,cmax(lim,dep[u]); 40 for(int i=head[u];i;i=Next[i]){ 41 int v=ver[i]; 42 if(v!=fa[u]){ 43 fa[v]=u,dfs1(v),sz[u]+=sz[v]; 44 if(sz[v]>sz[son[u]]) son[u]=v; 45 } 46 } 47 } 48 void dfs2(int u,int t){ 49 top[u]=t; 50 if(son[u]) dfs2(son[u],t);else return; 51 for(int i=head[u];i;i=Next[i]){ 52 int v=ver[i]; 53 if(v!=fa[u]&&v!=son[u]) dfs2(v,v); 54 } 55 } 56 inline int LCA(int u,int v){ 57 while(top[u]!=top[v]){ 58 if(dep[top[u]]