BZOJ 3522&4543

题意:求一棵树上有多少个三元组{x,y,z}满足x<y<z且两两距离相同。

解法1:

启发式合并,每个点继承最大的孩子的信息,用指针O(1)转移,其余孩子O(size)加入,这样可以维护子树中所有节点的深度信息和所有二元组{x,y},x,y深度相同且在同一棵子树中的个数。然后就可以算了,复杂度是O(nlogn)

#include<bits/stdc++.h>
#define N 1000100
using namespace std;
struct Node{
    vector<long long> lg;
    vector<long long> dis;
    int f;
    int size(){
        return dis.size();
    }
    void clear(){
        dis.clear();lg.clear();
        f=0;
    }
}*fr[N],re[N];
inline int cmp(int a,int b){
    return fr[a]->size()>fr[b]->size();
}
int n,len;
long long ans=0;
int to[N<<1],beg[N],nex[N<<1];
vector<int> s;
inline void Add(int a,int b){
    nex[++len]=beg[a],beg[a]=len,to[len]=b;
    nex[++len]=beg[b],beg[b]=len,to[len]=a;
}
void dfs(int p,int fa){
    for(int i=beg[p];i;i=nex[i])
        if(to[i]!=fa) dfs(to[i],p);
    s.clear();
    for(int i=beg[p];i;i=nex[i])
        if(to[i]!=fa) s.push_back(to[i]);
    if(!s.size()){
        fr[p]=re+p;
        fr[p]->dis.push_back(1);
        fr[p]->lg.push_back(0);
        return;
    }
    sort(s.begin(),s.end(),cmp);
    fr[p]=fr[s[0]];
    fr[p]->f++;
    int fp=fr[p]->f;
    fr[p]->lg.push_back(0);fr[p]->lg.push_back(0);
    fr[p]->dis.push_back(1);
    ans+=fr[p]->lg[fp];
    for(int i=1;i<s.size();i++){
        int v=s[i],fv=fr[v]->f;
        for(int i=0;i<=fv;i++)
            ans+=(fr[v]->dis[fv-i])*(fr[p]->lg[i+fp+1]);
        for(int i=1;i<=fv;i++)
            ans+=(fr[v]->lg[fv+i])*(fr[p]->dis[fp-i+1]);
        for(int i=0;i<=fv;i++)
            fr[p]->lg[i+fp+1]+=(fr[v]->dis[fv-i])*(fr[p]->dis[fp-i-1]);
        for(int i=1;i<=fv;i++)
            fr[p]->lg[i+fp-1]+=fr[v]->lg[i+fv];
        for(int i=0;i<=fv;i++)
            fr[p]->dis[fp-(i+1)]+=fr[v]->dis[fv-i];
    }
}
int main(){
    scanf("%d",&n);
    for(int i=1,a,b;i<n;i++){
        scanf("%d%d",&a,&b);
        Add(a,b);
    }
    for(int i=0;i<=n;i++) re[i].clear();
    dfs(1,0);
    printf("%lld\n",ans);
    return 0;
}

解法2:

长链剖分,待补全

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值