洛谷P2634 聪聪可可(BZOJ2152)

292 篇文章 1 订阅
281 篇文章 1 订阅

点分治

洛谷题目传送门

BZOJ题目传送门

简单的点分治应用,即统计距离%3=0的点对。

t[0],t[1],t[2] 分别为到根节点距离为0、距离为1、距离为2(%3意义下)的点数,则答案为 2t[1]t[2]+t[0]2

代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#define MAXN 20000
using namespace std;
struct edge{
    int next,to,dis;
}ed[MAXN*2+5];
int n,k,rt,t,p,ans;
int h[MAXN+5],sum[MAXN+5],dep[3],dis[MAXN+5],ma[MAXN+5];
bool f[MAXN+5];
inline char readc(){
    static char buf[100000],*l=buf,*r=buf;
    if (l==r) r=(l=buf)+fread(buf,1,100000,stdin);
    if (l==r) return EOF; return *l++;
}
inline int _read(){
    int num=0; char ch=readc();
    while (ch<'0'||ch>'9') ch=readc();
    while (ch>='0'&&ch<='9') { num=num*10+ch-48; ch=readc(); }
    return num;
}
void addedge(int x,int y,int z){
    ed[++k].next=h[x]; ed[k].to=y; ed[k].dis=z; h[x]=k;
}
void dfsrt(int x,int fa){
    sum[x]=1; ma[x]=0;
    for (int i=h[x];i;i=ed[i].next)
        if (ed[i].to!=fa&&!f[ed[i].to]){
            int v=ed[i].to; dfsrt(v,x);
            sum[x]+=sum[v]; ma[x]=max(ma[x],sum[v]);
        }
    ma[x]=max(ma[x],t-sum[x]);
    if (ma[x]<ma[rt]) rt=x;
}
void dfsdep(int x,int fa){
    dep[dis[x]%3]++;
    for (int i=h[x];i;i=ed[i].next)
        if (ed[i].to!=fa&&!f[ed[i].to]){
            dis[ed[i].to]=(dis[x]+ed[i].dis)%3;
            dfsdep(ed[i].to,x);
        }
}
int dfssum(int x,int v){
    dis[x]=v%3; dep[0]=dep[1]=dep[2]=0; dfsdep(x,0);
    return dep[1]*dep[2]*2+dep[0]*dep[0];
}
void dfsans(int x){
    f[x]=true; ans+=dfssum(x,0);
    for (int i=h[x];i;i=ed[i].next)
        if (!f[ed[i].to]){
            int v=ed[i].to; 
            ans-=dfssum(v,ed[i].dis);
            rt=0; t=sum[v];
            dfsrt(v,0); dfsans(rt);         
        }
}
int gcd(int a,int b){
    if (b==0) return a;
    return gcd(b,a%b);
}
int main(){
    n=_read();
    for (int i=1;i<n;i++){
        int u=_read(),v=_read(),d=_read()%3;
        addedge(u,v,d); addedge(v,u,d);
    }
    t=n; ma[0]=0x7fffffff;
    dfsrt(1,0); dfsans(rt);
    int r=gcd(ans,n*n);
    printf("%d/%d",ans/r,n*n/r);
    return 0;
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值