阴阳 详细题解+代码

来源:互联网 发布:访客网络不能限速 编辑:程序博客网 时间:2024/05/17 04:03

Description

Farmer John 正在在计划自己的农场漫步。他的农场的结构就像一棵树:农场有N个谷仓(1<= N <=100,000),分别由N-1条路链接。这样,他便可以通过这些谷仓间的道路遍及各个谷仓。Farmer John想要选择一条路线:这条路线的起点和终点分别为农场中两个不同的谷仓,这条路线不能重复经过一条边两次。Farmer John担心这条路径可能会偏长,所以他想在路线上寻找一个休息点(当然这个休息点不能为起点或者终点)。
每条边的两旁都是牛群,要么是Charcolais(白毛),要么是Angus(黑毛)。Farmer John是一个聪明人,所以他想要在他通过小路的同时平衡小路两侧阴阳的力量。他要选择一条路径使得他从起点到休息站,和从休息站到终点这两段路上都满足路两边的Charcolais牛群和Angus牛群总数量相同。
Farmer John好奇他能找到多少条如上所述的平衡的路径。我们认为,当且仅当两条路线的边的集合不同时,这两条路径才被认为是不同的,否则认为是相同的路线。就算路线上有多个有效的“休息站”的位置能使路线平衡,我们也只记为一条路线。
请帮助计算有多少条不同的平衡路线。

Input

第1行:包含一个整数N。
第2.. N行:每行包含三个整数a_i、b_i和t_i,表示第i条路径连接的两个谷仓a_i、b_i。t_i表示第i条路边上的牛群种类:0表示Charcolais,1表示Angus。

Output

输出仅一行,包含一个整数,表示可能存在的路径数目。

Sample Input

7
1 2 0
3 1 1
2 4 0
5 2 0
6 3 1
5 7 1

Sample Output

1

Data Constraint

1<= N <=100,000

Hint

不存在长度为2的路线,所以我们只能考虑长度为4的路线,路线3-1-2-5-7休息点设在2是一条平衡路线。

Solution

此题为思想题。思想够好,轻松过,思想不够好,看你卡多久。
把两种牛看做-1和1,作为边的边权。那么目标就是找到有多少对两个点中间的某个点到这两个的的边权和都为0。
树上问题一般用点分治,分几种情况考虑

1.整条路都在子树中

是最简单的,直接递归下去即可。

2.当前的根节点为一条路的端点

首先设两个数组dis和p。dis[x]表示x到当前的根节点的边权和。p[x]表示dis[x]在x的祖先中(不包括根节点)是否出现过。
那么当dis[x]=0且p[x]=1时,就是这种情况,ans++。
此后几乎每种情况都会用到dis和p。

3.根节点仅为路径上的一点,不是端点也不是休息点

那么对于两个点x和y,只要它们的dis相加等于0,并且能找到一个休息点,那就是一条路径。这时你应该已经发现,如果p[x]为1 的话,x到根节点就必定有一个点可以做休息点,这也是第二种情况可以那么判断的原因。
对于这种情况,则只需有一个点的p为1就行了。
当然,直接两两点枚举的话,时间复杂度就变成n2了,所以加一点小东西。
我用nd[x]表示已经做到的子树中dis[]为x的总个数。dis有可能是负数,在c++的处理里稍微麻烦一点。那么当你做到的点x的p为1的时候,ans+=nd[-dis[x]]。
再用np[x]表示已经做到的子树中dis[]为x且这个点的p为1的个数,那么当你做到的点x的p为1的时候,ans+=np[-dis[x]]。
在做到当前根的第二个子树时,就会把这个子树的每个点与第一个子树的每个点匹配,然后把第二个子树统计。那么在做到第三个子树时,就会把前两个子树一起匹配了。

4.根节点仅为路径上的一点,而且是休息点

这种情况比较难处理。设当前点为x,那么必须p[x]=0否则就会算重。那么当dis[x]=0而且p[x]=0时,就可以匹配点了。目标点必须也是同样的状态,也就是p=0,于是ans+=nd[0]-np[0]

完美解决

此题细节较多,需要注意。
另外,各种数组不能用fillchar或memset来归零,会超时,只能在求数组的时候顺便处理一下。
如果你能一次过,那么你就是大神。

Code

#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#define N 111000#define fo(i,a,b) for(int i=a;i<=b;i++)#define ll long long#define g(a) (a>=0?a:-a+N)using namespace std;int dis[N],p[N],next[N*10],last[N],to[N*10],data[N*10],tot=0,n,bz[N],root,size[N],ms[N],bd[N*10],nd[N*10],np[N*2];ll ans=0;void putin(int x,int y,int z){    next[++tot]=last[x];last[x]=tot;to[tot]=y;data[tot]=z;}void getroot(int x,int fa,int n){    size[x]=1;ms[x]=0;    for(int i=last[x];i;i=next[i])    {        int y=to[i];        if (y==fa || bz[y]) continue;        getroot(y,x,n);size[x]+=size[y];        ms[x]=max(ms[x],size[y]);    }    ms[x]=max(ms[x],n-size[x]);    if (ms[x]<ms[root]) root=x;}void getdis(int x,int fa,int bz2,int n){    nd[g(dis[x])]=0;np[g(dis[x])]=0;nd[g(-dis[x])]=0;np[g(-dis[x])]=0;    int jy=bd[g(dis[x])];    if (fa!=bz2)    {        if (bd[g(dis[x])]) p[x]=1;        else p[x]=0;        bd[g(dis[x])]=1;    }    for(int i=last[x];i;i=next[i])    {        int y=to[i];        if(y==fa || bz[y]) continue;        dis[y]=dis[x]+data[i];        getdis(y,x,bz2,n);    }    if (fa!=0) bd[g(dis[x])]=jy;}void dg1(int x,int fa){    if (dis[x]==0 && p[x]) ans++;    if (p[x]==1) ans+=nd[g(-dis[x])];    else ans+=np[g(-dis[x])];    if (dis[x]==0 && p[x]==0) ans+=nd[0]-np[0];    for(int i=last[x];i;i=next[i])    {        int y=to[i];        if(y==fa || bz[y]) continue;        dg1(y,x);    }}void dg2(int x,int fa){    nd[g(dis[x])]++;np[g(dis[x])]+=p[x];    for(int i=last[x];i;i=next[i])    {        int y=to[i];        if(y==fa || bz[y]) continue;        dg2(y,x);    }}void dg(int x,int fa,int n){    bz[x]=1;dis[x]=0;getdis(x,fa,fa,n);    for(int i=last[x];i;i=next[i])    {        int y=to[i];        if(y==fa || bz[y]) continue;        dg1(y,x);        dg2(y,x);       }    for(int i=last[x];i;i=next[i])    {        int y=to[i];        if(y==fa || bz[y]) continue;        int jy=size[y];root=0;getroot(y,x,jy);        dg(root,x,size[y]);    }}int main(){    scanf("%d",&n);    fo(i,1,n-1)    {        int x,y,z;scanf("%d%d%d",&x,&y,&z);        if (z==0) z=-1;        putin(x,y,z);putin(y,x,z);    }    ms[0]=2147483647;root=0;getroot(1,0,n);    dg(root,0,n);    printf("%lld\n",ans);}
1 0
原创粉丝点击