jzoj 3234. 阴阳

38 篇文章 0 订阅
32 篇文章 0 订阅

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是一条平衡路线。

 

解析(部分摘自ZLT大佬的文)

很显然这是一道点分治,(我怎么没看出来?~_~?) 

关键是一条路径上要找到一个分割点,然后两边的路都要是两种牛数量相同的。

好了分析一下吧,我们可以首先把不同牛看成是-1和1的权值。然后要找一条路径,和一个割点,使两端平衡嘛。

我们先定个根吧,当根为rx时,我们从根上发射出size_rx条路径,我们称为分路径。

现在我们要干的就是配对了。

这道题可以直接开个桶做,两条路径权值加起来等于0,就有可能在答案中。

那么现在问题来了,怎么样的路径才是合法的呢?

我们先考虑分路径。只要分路径上有一个点的权值是和端点的一样的,那么这条分路径就是有割点的了。

我们知道,由两条分路径合起来的路径,只要有一条是有割点的,这条路就合法了,淡然两个都有割点更不用说。所以就可以统计了。

还有一些特殊情况,这个就不说了。

对于一个子树,处理必须经过他路径的复杂度是size_rx的,所以就可以点分治了。

时间复杂度:O(N log N)——优秀

CODE(嘿嘿,这也是ZLT大佬的biu)

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int S=100005;
const int N=500005;
struct rec
{
    int sig,cnt;//cnt equal pos
}buc[N*2][2],tr[N];
int dis[N],first[N*2],next[N*2],b[N*2],pd[N],c[N*2],st[N],en[N],i,x,y,t,n,t1,tt,ttt,t2,col,vis[N],f[N],cnt[N],up[N],go[N];
ll ans;
int cr(int x,int y,int t)
{
    ttt++;
    b[ttt]=y;
    c[ttt]=(t==1)?1:-1;
    next[ttt]=first[x];
    first[x]=ttt;
}
int dfs(int x,int y)
{
    tt++;
    tr[tt].cnt=dis[x];
    if (pd[dis[x]]!=0)
        tr[tt].sig=1;
    else
        tr[tt].sig=0;
    if (pd[dis[x]]==0)
        pd[dis[x]]=x;
    for(int p=first[x];p;p=next[p])
    {
        if(b[p]!=y&&!vis[b[p]])
        {
            dis[b[p]]=dis[x]+c[p];
            dfs(b[p],x);
        }
    }
    if (pd[dis[x]]==x)
        pd[dis[x]]=0;
}
int thr(int x,int y)
{
    cnt[x]=1;
    for (int p=first[x];p;p=next[p])
    {
        if (b[p]!=y&&!vis[b[p]])
        {
            thr(b[p],x);
            cnt[x]+=cnt[b[p]];
        }
    }
}
int find(int x,int y)
{
    int ret=x,tmp;
    f[x]=up[x];
    for(int p=first[x];p;p=next[p])
        if (b[p]!=y&&!vis[b[p]])
            f[x]=max(cnt[b[p]],f[x]);
    for(int p=first[x];p;p=next[p])
        if (b[p]!=y&&!vis[b[p]])
        {
            up[b[p]]=up[x]+cnt[x]-cnt[b[p]];
            tmp=find(b[p],x);
            if (f[ret]>f[tmp]) ret=tmp;
        }
    return ret;
}
ll getans()
{
    ll ret=0;
    for(int i=1;i<=t2;i++)
        for(int j=st[i];j<=en[i];j++)
        {
            if (buc[tr[j].cnt+S][tr[j].sig].sig!=col)
            {
                buc[tr[j].cnt+S][tr[j].sig].sig=col;
                buc[tr[j].cnt+S][tr[j].sig].cnt=1;
            }
            else
                buc[tr[j].cnt+S][tr[j].sig].cnt++;
        }
    for(int i=1;i<=t2;i++)
    {
        for(int j=st[i];j<=en[i];j++)   buc[tr[j].cnt+S][tr[j].sig].cnt--;
        for(int j=st[i];j<=en[i];j++)
        {
            if (tr[j].cnt==0&&tr[j].sig==1) ret++;
            if (buc[-tr[j].cnt+S][1].sig==col)
                ret+=buc[-tr[j].cnt+S][1].cnt;
            if ((tr[j].sig==1||tr[j].cnt==0)&&(buc[-tr[j].cnt+S][0].sig==col))
                ret+=buc[-tr[j].cnt+S][0].cnt;
        }
    }
    return ret;
}

int solve(int x,int y)
{
    //printf("%d\n",&x);
    vis[x]=1;
    tt=0;
    t2=0;
    for(int p=first[x];p;p=next[p])
    {
        if (vis[b[p]]==0)
        {
            //pd[0]=x;
            t2++;
            st[t2]=en[t2-1]+1;
            dis[b[p]]=dis[x]+c[p];
            dfs(b[p],x);
            en[t2]=tt;
            //pd[0]=0;
        }
    }
    col++;
    ans+=getans();
    for(int p=first[x];p;p=next[p])
    {   
        if (vis[b[p]]==0)
        {
            thr(b[p],x);
            t1++;
            up[b[p]]=0;
            go[t1]=find(b[p],x);
            dis[go[t1]]=0;
            solve(go[t1],x);
        }
    }
}
int main()
{
    freopen("angus.in","r",stdin);
    freopen("angus.out","w",stdout);
    scanf("%d",&n);
    for(i=1;i<n;i++)
    {
        scanf("%d%d%d",&x,&y,&t);
        cr(x,y,t);
        cr(y,x,t);
    }
    thr(1,0);
    t1++;
    go[1]=find(1,0);
    dis[go[1]]=0;
    solve(go[1],0);
    printf("%lld",ans);
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值