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 }

 

posted @ 2018-12-27 14:36  guapisolo  阅读(233)  评论(0编辑  收藏  举报