BZOJ2512 聪聪可可 - 点分治

10 篇文章 0 订阅

传送门

题解:点分治模板题

代码:

//BZOJ 2152
#include<iostream>
#include<cstring>
#include<cstdio>
#define MAXN 20010
#define MAXM 40010
#define cls(x) cnt[x][0]=cnt[x][1]=cnt[x][2]=0;
#define debug(x) cerr<<#x<<"="<<x
#define sp <<" "
#define ln <<endl
using namespace std;
struct edges{
	int to,pre,wgt;
}e[MAXM];int etop,h[MAXN],cnt[MAXN][3],ans[MAXN];
inline int add_edge(int u,int v,int w)
{
	etop++;
	e[etop].pre=h[u];
	h[u]=etop;
	e[etop].wgt=w;
	e[etop].to=v;
	return 0;
}
int rt,sz[MAXN],maxsz[MAXN];
bool vis[MAXN];int full_sz;
int get_rt(int x,int f)
{
	sz[x]=1,maxsz[x]=0;
	for(int i=h[x];i;i=e[i].pre)
		if(!vis[e[i].to]&&e[i].to^f)
		{
			get_rt(e[i].to,x);
			sz[x]+=sz[e[i].to];
			if(sz[e[i].to]>maxsz[x])
				maxsz[x]=sz[e[i].to];
		}
	maxsz[x]=max(maxsz[x],full_sz-sz[x]);
	if(maxsz[x]<maxsz[rt]) rt=x;return 0;
}
int getsz(int x,int f)
{
	sz[x]=1;
	for(int i=h[x];i;i=e[i].pre)
		if(!vis[e[i].to]&&e[i].to^f)
			sz[x]+=getsz(e[i].to,x);
	return sz[x];
}
int calc(int x,int f,int w)
{
	cls(x);cnt[x][w]=1;
	for(int i=h[x];i;i=e[i].pre)
		if(!vis[e[i].to]&&e[i].to!=f)
		{
			calc(e[i].to,x,(w+e[i].wgt)%3);
			for(int p=0;p<3;p++)
				cnt[x][p]+=cnt[e[i].to][p];
		}
	return 0;
}
int dfs(int x)
{
	get_rt(x,rt=0);getsz(x=rt,0);
	vis[x]=true;cls(x);cnt[x][0]=ans[x]=1;
	for(int i=h[x];i;i=e[i].pre)
		if(!vis[e[i].to])
		{
			int y;calc(y=e[i].to,x,e[i].wgt);
			for(int p=0;p<3;p++)
				ans[x]+=cnt[x][p]*cnt[y][(3-p)%3];
			for(int p=0;p<3;p++) cnt[x][p]+=cnt[y][p];
		}
//	debug(cnt[x][0])sp,debug(cnt[x][1])sp,debug(cnt[x][2])ln;
//	debug(x)sp,debug(ans[x])ln;
	for(int i=h[x];i;i=e[i].pre)
		if(!vis[e[i].to])
		{
			full_sz=sz[e[i].to];
			dfs(e[i].to);
		}
	return 0;
}
inline int gcd(int a,int b)
{
	return a?gcd(b%a,a):b;
}
int main()
{
	int n;scanf("%d",&n);
	for(int i=1;i<n;i++)
	{
		int u,v,w;scanf("%d%d%d",&u,&v,&w);w%=3;
		add_edge(u,v,w);add_edge(v,u,w);
	}
	full_sz=n;maxsz[0]=n+1;dfs(1);
	int a=0,b=n*n;
	for(int i=1;i<=n;i++) a+=ans[i];a-=n;a<<=1;a+=n;
	int d=gcd(a,b);printf("%d/%d\n",a/d,b/d);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值