BZOJ 3697/3127 采药人的路径 (点分治)
题目大意:
从前有一棵无向树,树上边权均为$0$或$1$,有一个采药人,他认为如果一条路径上边权为$0$和$1$的边数量相等,那么这条路径阴阳平衡。他想寻找一条合法的采药路径,保证阴阳平衡。然后他发现采药很累,于是乎他需要保证这条路径上有一个中转站,路径两个端点到中转站的路径都需要阴阳平衡 $n \leqslant 10^{5}$,求合法路径数
把$0$边边权变成$-1$,易发现如果一条路径阴阳平衡,那么边权总和为$0$
由于是树上路径的计数问题,考虑树分治,每次选中心作为根,设某点$x$到根的路径的边权和为$dis_{x}$
把中转站加进去会发生什么呢?
如果某个点$x$到根的路径上能设置中转站,那么根到$x$的路径上一定存在一点$y$,$dis_{y}=dis_{x}$,即$x$到$y$的路径$dis$为$0$,这个操作可以用桶实现
现在要在根统计答案了,显然每个点分为种情况,到根的路径能设置中转站和不能,用桶分别计数即可,设为$f[i][0]$和$f[i][1]$,表示$dis=i$时的方案数
发现还有$dis<0$的情况,需要额外记录一个数组$g$
那么答案就是$\sum_{i=1} f[i][0]*g[i][1]+f[i][1]*g[i][0]+f[i][1]*g[i][1]$
还要去掉不合法的情况,在当前根的每个子节点依然进行上述方法计数即可
$dis=0$的情况需要单独讨论,所有$dis=0$的路径都能两两匹配。此外,不以根节点为中转站,且$dis=0$的路径也一定合法
细节比较多
1 #include <cmath> 2 #include <vector> 3 #include <cstdio> 4 #include <cstring> 5 #include <algorithm> 6 #define N1 100010 7 #define M1 (N1<<1) 8 #define ll long long 9 #define inf 233333333 10 using namespace std; 11 12 int gint() 13 { 14 int ret=0,fh=1;char c=getchar(); 15 while(c<'0'||c>'9'){if(c=='-')fh=-1;c=getchar();} 16 while(c>='0'&&c<='9'){ret=ret*10+c-'0';c=getchar();} 17 return ret*fh; 18 } 19 int n; 20 21 struct Edge{ 22 int to[M1],nxt[M1],val[M1],head[N1],cte; 23 void ae(int u,int v,int w) 24 {cte++;to[cte]=v,nxt[cte]=head[u],val[cte]=w,head[u]=cte;} 25 }E; 26 27 int use[N1],mi,G,S,tsz,ma; 28 int sf[N1],sg[N1],ms[N1],sz[N1],dis[N1]; 29 int f[N1][2],g[N1][2]; ll ans; int que[N1],tl; 30 //int dep[N1],fa[N1],de; 31 void dfs_sum(int u,int dad) 32 { 33 que[++tl]=u; 34 if(dis[u]>=0){ 35 if(sf[dis[u]]) f[dis[u]][1]++; 36 else f[dis[u]][0]++; 37 sf[dis[u]]++; ma=max(ma,dis[u]); 38 if(u==S) sf[0]--,f[0][0]--; 39 }else{ 40 if(sg[-dis[u]]) g[-dis[u]][1]++; 41 else g[-dis[u]][0]++; 42 sg[-dis[u]]++; ma=max(ma,-dis[u]); 43 } 44 for(int j=E.head[u];j;j=E.nxt[j]) 45 { 46 if(E.to[j]==dad||use[E.to[j]]) continue; 47 dis[E.to[j]]=dis[u]+E.val[j]; 48 dfs_sum(E.to[j],u); 49 } 50 if(dis[u]>=0) sf[dis[u]]--; 51 else sg[-dis[u]]--; 52 } 53 void gra(int u,int dad) 54 { 55 sz[u]=1; ms[u]=0; 56 for(int j=E.head[u];j;j=E.nxt[j]) 57 { 58 int v=E.to[j]; 59 if(v==dad||use[v]) continue; 60 gra(v,u); sz[u]+=sz[v]; 61 ms[u]=max(ms[u],sz[v]); 62 } 63 ms[u]=max(ms[u],tsz-sz[u]); 64 if(ms[u]<ms[G]) G=u; 65 } 66 void clr() 67 { 68 int x; 69 while(tl) 70 { 71 x=que[tl]; tl--; 72 if(dis[x]>=0) f[dis[x]][0]=f[dis[x]][1]=0; 73 else g[-dis[x]][0]=g[-dis[x]][1]=0; 74 } 75 sg[0]=sf[0]=0; 76 } 77 void calc(int u,int type) 78 { 79 ma=0; dfs_sum(u,-1); 80 ans+=( 1ll*f[0][0]*(f[0][0]-1)/2 + 1ll*f[0][1]*(f[0][1]-1)/2 + 1ll*f[0][0]*f[0][1] )*type; 81 for(int i=1;i<=ma;i++) 82 ans+=( 1ll*f[i][0]*g[i][1] + 1ll*f[i][1]*g[i][0] + 1ll*f[i][1]*g[i][1])*type; 83 if(type==1) ans+=f[0][1]; 84 clr(); 85 } 86 void main_dfs(int u) 87 { 88 int j,v; use[u]=1; S=u; dis[u]=0; calc(u,1); 89 for(j=E.head[u];j;j=E.nxt[j]) 90 { 91 v=E.to[j]; if(use[v]) continue; 92 G=0; tsz=sz[v]; gra(v,u); 93 calc(v,-1); 94 main_dfs(G); 95 } 96 } 97 98 int main() 99 { 100 //freopen("t2.in","r",stdin); 101 int i,x,y,z; 102 scanf("%d",&n); 103 for(i=1;i<n;i++) 104 { 105 x=gint(),y=gint(),z=gint(); 106 z=((z)?1:-1); 107 E.ae(x,y,z),E.ae(y,x,z); 108 } 109 ms[0]=tsz=n; G=0; gra(1,-1); gra(G,-1); 110 main_dfs(G); 111 printf("%lld\n",ans); 112 return 0; 113 }