阴阳

Description

给出一棵n个点的树,每条边有边权1或-1.一条合法的路径(st,en)指路径上存在一个异于端点的点x,使得dis(st,x)=dis(x,en)=0.(dis不解释),求合法路径的数量。
n<=10^5

Solution

学习(复习)点分治
考虑经过当前重心x的合法路径,可以发现,满足这样的路径都是dis(st,x)+dis(x,en)=0且st到x的路径中有一个点k满足dis(k,x)=dis(st,x),或en到x的路径中有这样的点。
那我们可以处理出所有的d[i]表示dis(x,i),然后开一个桶来计算它可以和多少对点配对。
设bz[i]=1表示i到x的路径中有一个点k满足d[k]=d[i],那么
当bz[i]=1&d[i]=0时,i点可以和x配对。
所有d[i]=0的点都可以互相配对。
细节特别多(纯粹来练练手)

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define rep(i,a) for(int i=last[a];i;i=next[i])
#define N 100005
using namespace std;
typedef long long ll;
struct note{int v,s,x;}d[N];
bool cmp(note x,note y) {return x.s<y.s;}
int n,l,x,y,z,tot,dis[N],size[N],yes[N],no[N],p[N*2];
int t[N*2],next[N*2],v[N*3],last[N];
bool bz[N];
ll ans;
void add(int x,int y,int z) {
    t[++l]=y;v[l]=z;next[l]=last[x];last[x]=l;
}
void get_size(int x,int y) {
    size[x]=1;
    rep(i,x) if (!bz[t[i]]&&t[i]!=y) {
        get_size(t[i],x);
        size[x]+=size[t[i]];
    }
}
void get_heavy(int x,int y) {
    bool f=0;
    rep(i,x) if (!bz[t[i]]&&t[i]!=y) {
        get_heavy(t[i],x);
        if (size[t[i]]>tot/2) f=1;
    }
    if (tot-size[x]>tot/2) f=1;
    if (!f) z=x;
}
void get_ans(int x,int y) {
    if (!dis[x]&&p[N]) ans++;
    ans+=yes[N-dis[x]];
    if (p[N+dis[x]]) ans+=no[N-dis[x]];
    p[N+dis[x]]++;
    rep(i,x) if (t[i]!=y&&!bz[t[i]]) {
        dis[t[i]]=dis[x]+v[i];
        get_ans(t[i],x);
    }
    p[N+dis[x]]--;
}
void updata(int x,int y) {
    if (!dis[x]||p[N+dis[x]]) yes[N+dis[x]]++;
    else no[N+dis[x]]++;
    p[N+dis[x]]++;
    rep(i,x) if (t[i]!=y&&!bz[t[i]]) updata(t[i],x);
    p[N+dis[x]]--;
}
void clear(int x,int y) {
    yes[dis[x]+N]=no[dis[x]+N]=0;
    rep(i,x) if (!bz[t[i]]&&t[i]!=y) clear(t[i],x);
}
void dfs(int x) {
    get_size(x,0);tot=size[x];
    get_heavy(x,0);bz[z]=1;
    rep(i,z) if (!bz[t[i]]) {
        dis[t[i]]=v[i];
        get_ans(t[i],z);
        updata(t[i],z);
    }
    clear(z,0);
    rep(i,z) if (!bz[t[i]]) dfs(t[i]);
}
int main() {
    scanf("%d",&n);
    fo(i,1,n-1) {
        scanf("%d%d%d",&x,&y,&z);
        if (!z) z=-1;
        add(x,y,z);add(y,x,z);
    }
    dfs(1);
    printf("%lld",ans);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值