首页 > 代码库 > BZOJ 2152 聪聪可可 树的点分治

BZOJ 2152 聪聪可可 树的点分治

题目大意:有两个小孩在玩游戏,他们每一个人在树中取一个点,如果这两个点之间的路径长度之和是3的倍数,那么聪聪就赢了,否则他就输了。给出这棵树,求聪聪赢的概率,答案用分数表示。


思路:数据范围2w,肯定不能枚举点然后LCA。所以就只能点分治了。这还是一道比较常规的点分治问题,但是有一个地方需要注意,在统计两点之间的距离的时候我一开始的想法是直接n^2的枚举,然后记录。但是那样时间复杂度就会严重退化。但是注意到我们只需要%3==0的数的个数,和这个有关的之后%3==0,%3==1,%3==2的数。所以在统计的时候我们只要统计这三种数字的个数就可以了。

PS:用了algorithm中的__gcd函数,据说考试的时候不让用。。。


CODE:


#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define MAX 20010
#define INF 0x3f3f3f3f
using namespace std;

int points;
int head[MAX],total;
int next[MAX << 1],aim[MAX << 1],length[MAX << 1];

int size[MAX],_size,_total,c;
bool v[MAX];
int cnt[MAX],dis[3],p;

inline void Add(int x,int y,int len)
{
	next[++total] = head[x];
	aim[total] = y;
	length[total] = len % 3;
	head[x] = total;
}

inline void GetRoot(int x,int last)
{
	size[x] = 1;
	int max_size = 0;
	for(int i = head[x]; i; i = next[i]) {
		if(aim[i] == last || v[aim[i]])	continue;
		GetRoot(aim[i],x);
		size[x] += size[aim[i]];
		max_size = max(max_size,size[aim[i]]);
	}
	max_size = max(max_size,_total - size[x]);
	if(_size > max_size)
		_size = max_size,c = x;
}

inline void GetDis(int x,int last,int len)
{
	++dis[len % 3];
	for(int i = head[x]; i; i = next[i]) {
		if(aim[i] == last || v[aim[i]])	continue;
		GetDis(aim[i],x,len + length[i]);
	}
}

inline int Count(int x,int len)
{
	int re = 0;
	p = 0;
	memset(dis,0,sizeof(dis));
	GetDis(x,0,len);
	re += dis[0] * dis[0];
	re += dis[1] * dis[2] << 1;
	return re;
}

inline void Work(int x)
{
	_size = INF;
	_total = size[x] ? size[x]:points;
	GetRoot(x,0);
	x = c;
	v[x] = true;
	cnt[x] = Count(x,0);
	for(int i = head[x]; i; i = next[i]) {
		if(v[aim[i]])	continue;
		cnt[x] -= Count(aim[i],length[i]);
		Work(aim[i]);
	}
}

int main()
{
	cin >> points;
	for(int x,y,z,i = 1; i < points; ++i) {
		scanf("%d%d%d",&x,&y,&z);
		Add(x,y,z),Add(y,x,z);
	}
	Work(1);
	int ans = 0;
	for(int i = 1; i <= points; ++i)
		ans += cnt[i];
	cout << ans / __gcd(ans,points * points) << '/' << points * points / __gcd(ans,points * points) << endl;
	return 0;
}




BZOJ 2152 聪聪可可 树的点分治