BZOJ 2152 聪聪可可 树的点分治

来源:互联网 发布:js是什么文件可以执行 编辑:程序博客网 时间:2024/05/22 00:53

题目大意:有两个小孩在玩游戏,他们每一个人在树中取一个点,如果这两个点之间的路径长度之和是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 0x3f3f3f3fusing 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;}




0 0
原创粉丝点击