BZOJ 2152 聪聪可可 树的点分治/树形DP

25 篇文章 1 订阅
11 篇文章 0 订阅

题目大意:给定一棵树,每条边上有边权,求距离为3的倍数的有序点对

树的点分治,对于每个重心统计出每棵子树距离重心长度为0/1/2的点的数量,计算出ans即可

最后ans*2+1 和n^2进行一下约分即可

其实我上当了……这题根本就没必要写树的点分治,直接树形DP就出来了

开一个三元组记录某棵子树中距离子树的根节点距离为某值的点的个数

然后直接统计+转移就行了


树的点分治:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 20200
using namespace std;
struct abcd{
	int to,f,next;
	bool ban;
}table[M<<1];
int head[M],tot=1;
int n,ans;
int siz[M];
void Add(int x,int y,int z)
{
	table[++tot].to=y;
	table[tot].f=z;
	table[tot].next=head[x];
	head[x]=tot;
}
void Get_Centre_Of_Gravity(int x,int from,int size,int &cg)
{
	int i;
	siz[x]=1;
	bool flag=1;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].ban||table[i].to==from)
			continue;
		Get_Centre_Of_Gravity(table[i].to,x,size,cg);
		if(siz[table[i].to]>size>>1)
			flag=0;
		siz[x]+=siz[table[i].to];
	}
	if(size-siz[x]>size>>1)
		flag=0;
	if(flag)
		cg=x;
}
void DFS(int x,int from,int dis,int cnt[])
{
	int i;
	siz[x]=1;
	cnt[dis]++;
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].ban||table[i].to==from)
			continue;
		DFS(table[i].to,x,(dis+table[i].f)%3,cnt);
		siz[x]+=siz[table[i].to];
	}
}
void Calculate(int x)
{
	int i;
	int cnt[3]={1,0,0};
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].ban)
			continue;
		int _cnt[3]={0};
		DFS(table[i].to,x,table[i].f,_cnt);
		ans+=cnt[0]*_cnt[0];
		ans+=cnt[1]*_cnt[2];
		ans+=cnt[2]*_cnt[1];
		cnt[0]+=_cnt[0];
		cnt[1]+=_cnt[1];
		cnt[2]+=_cnt[2];
	}
}
void Tree_Divide_And_Conquer(int root,int size)
{
	int i,cg;
	if(size==1)
		return;
	Get_Centre_Of_Gravity(root,0,size,cg);
	Calculate(cg);
	for(i=head[cg];i;i=table[i].next)
	{
		if(table[i].ban)
			continue;
		table[i].ban=table[i^1].ban=1;
		Tree_Divide_And_Conquer(table[i].to,siz[table[i].to]);
	}
}
int GCD(int x,int y)
{
	return y?GCD(y,x%y):x;
}
int main()
{

	int i,x,y,z;
	cin>>n;
	for(i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Add(x,y,z%3);
		Add(y,x,z%3);
	}
	Tree_Divide_And_Conquer(1+n>>1,n);
	ans=ans*2+n;
	int gcd=GCD(ans,n*n);
	cout<<ans/gcd<<'/'<<n*n/gcd<<endl;
}

树形DP:

#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#define M 20200
using namespace std;
struct triple{
	int x,y,z;
	triple(int _=0,int __=0,int ___=0):x(_),y(__),z(___){}
	void Rotate()
	{
		swap(x,y);
		swap(x,z);
	}
	void operator += (const triple &_)
	{
		x+=_.x;
		y+=_.y;
		z+=_.z;
	}
};
struct abcd{
	int to,f,next;
}table[M<<1];
int head[M],tot;
int n,ans;
triple f[M];
void Add(int x,int y,int z)
{
	table[++tot].to=y;
	table[tot].f=z;
	table[tot].next=head[x];
	head[x]=tot;
}
void Tree_DP(int x,int from)
{
	int i;
	f[x]=triple(1,0,0);
	for(i=head[x];i;i=table[i].next)
	{
		if(table[i].to==from)
			continue;
		Tree_DP(table[i].to,x);
		for(;table[i].f;table[i].f--)
			f[table[i].to].Rotate();
		ans+=f[x].x*f[table[i].to].x;
		ans+=f[x].y*f[table[i].to].z;
		ans+=f[x].z*f[table[i].to].y;
		f[x]+=f[table[i].to];
	}
}
int main()
{
	int i,x,y,z;
	cin>>n;
	for(i=1;i<n;i++)
	{
		scanf("%d%d%d",&x,&y,&z);
		Add(x,y,z%3);
		Add(y,x,z%3);
	}
	Tree_DP(1,0);
	ans=ans*2+n;  
    int gcd=__gcd(ans,n*n);  
    cout<<ans/gcd<<'/'<<n*n/gcd<<endl;  
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值