正题
题目链接:https://www.luogu.org/problemnew/show/P3085
题目大意
一棵树只有−1-1−1和111的边权,询问有多少条路径可以找到一个分界点使得分出的两条路径长度为000。
解题思路
询问路径显然点分治一波。
然后考虑分界点的两种情况
- 分界点在当前扫描到的路径
- 分界点在之前扫描过的路径经过
对于情况111我们有数组bib_ibi表示在根到该节点的路径中是否存在长度为iii的边,然后用g2ig2_ig2i表示之前扫描过的子树中存在多少长度为iii的路径且中间没有分界点(这里的分界点指的是有一个点的did_idi等于它的did_idi)。
对于情况222我们用gig_igi表示之前的子树中存在多少长度为iii的路径且中间有分界点(这里的概念同上)。
然后每次进行两次dfsdfsdfs,一次统计ansansans,一次修改ggg和g2g2g2即可。
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=1e5+1,M=N*2;
struct Edge_node{int to,next,w;
}a[M];
int n,m,ls[N],siz[N],f[M],g[M],g2[M];
int b[M],tot,num,root,maxs,mins,sum;
long long ans;
bool v[N];
void addl(int x,int y,int w)
{a[++tot].to=y;a[tot].next=ls[x];ls[x]=tot;a[tot].w=w*2-1;
}
void groot(int x,int fa)
{siz[x]=1;f[x]=0;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(y==fa||v[y]) continue;groot(y,x);siz[x]+=siz[y];f[x]=max(f[x],siz[y]);}f[x]=max(f[x],num-f[x]);if(f[x]<f[root]) root=x;
}
void dfs(int x,int fa,int w)
{maxs=max(maxs,w);mins=min(mins,w);ans+=g[M-w];if(b[w]) ans+=(long long)g2[M-w];if(w==N) ans+=(long long)(b[w]>1);b[w]++;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(v[y]||y==fa) continue;dfs(y,x,w+a[i].w);;}b[w]--;
}
void Change(int x,int fa,int w)
{++(b[w]?g[w]:g2[w]);b[w]++;for(int i=ls[x];i;i=a[i].next){int y=a[i].to;if(v[y]||y==fa) continue;Change(y,x,w+a[i].w);}b[w]--;
}
void solve(int x)
{v[x]=1;int y;b[mins=maxs=N]=1; for(int i=ls[x];i;i=a[i].next)if(!v[y=a[i].to])dfs(y,x,N+a[i].w),Change(y,x,N+a[i].w);memset(g+mins,0,(maxs-mins+1)<<2);memset(g2+mins,0,(maxs-mins+1)<<2);for(int i=ls[x];i;i=a[i].next)if(!v[y=a[i].to])root=0,groot(y,x),num=siz[y],solve(root);
}
int main()
{scanf("%d",&n);for(int i=1;i<n;i++){int x,y,w;scanf("%d%d%d",&x,&y,&w);addl(x,y,w);addl(y,x,w);}f[0]=2147483647;num=n;groot(1,1);solve(root);printf("%lld",ans);
}