洛谷 P2634 [国家集训队]聪聪可可-树分治(点分治,容斥版) +读入挂+手动O2优化吸点氧才过。。。-树上路径为3的倍数的路径数量

P2634 [国家集训队]聪聪可可

题目描述

聪聪和可可是兄弟俩,他们俩经常为了一些琐事打起来,例如家中只剩下最后一根冰棍而两人都想吃、两个人都想玩儿电脑(可是他们家只有一台电脑)……遇到这种问题,一般情况下石头剪刀布就好了,可是他们已经玩儿腻了这种低智商的游戏。

他们的爸爸快被他们的争吵烦死了,所以他发明了一个新游戏:由爸爸在纸上画n个“点”,并用n-1条“边”把这n个“点”恰好连通(其实这就是一棵树)。并且每条“边”上都有一个数。接下来由聪聪和可可分别随即选一个点(当然他们选点时是看不到这棵树的),如果两个点之间所有边上数的和加起来恰好是3的倍数,则判聪聪赢,否则可可赢。

聪聪非常爱思考问题,在每次游戏后都会仔细研究这棵树,希望知道对于这张图自己的获胜概率是多少。现请你帮忙求出这个值以验证聪聪的答案是否正确。

输入格式

输入的第1行包含1个正整数n。后面n-1行,每行3个整数x、y、w,表示x号点和y号点之间有一条边,上面的数是w。

输出格式

以即约分数形式输出这个概率(即“a/b”的形式,其中a和b必须互质。如果概率为1,输出“1/1”)。

输入输出样例

输入 #1
5
1 2 1
1 3 2
1 4 1
2 5 3
输出 #1
13/25

说明/提示

【样例说明】

13组点对分别是(1,1) (2,2) (2,3) (2,5) (3,2) (3,3) (3,4) (3,5) (4,3) (4,4) (5,2) (5,3) (5,5)。

【数据规模】

对于100%的数据,n<=20000。

 

题意就是找树上任意两点路径为3的倍数的路径数,除以所有路径数。

点分治,最后两组数据怎么也过不了,今天过了。

换了个读入挂,也不行,手动吸氧过了。

不知道为什么老是超时,有点躁。

 

关于C++手动开O2优化:

  • O2优化能使程序的编译效率大大提升

  • 从而减少程序的运行时间,达到优化的效果。

  • C++程序中的O2开关如下所示:

  • #pragma GCC optimize(2)
  • 只需将这句话放到程序的开头即可打开O2优化开关。

 

一开始手动吸氧也没过,后来可能网络稳了,试了很多次都过了。

 

代码:

  1 //点分治
  2 #include<bits/stdc++.h>
  3 using namespace std;
  4 #define fin freopen("in.txt", "r", stdin)
  5 #define fout freopen("out.txt", "w", stdout)
  6 #define FI(n) FastIO::read(n)
  7 #pragma GCC optimize(2)//O2优化,手动吸氧才过。。。
  8 typedef long long ll;
  9 const int inf=0x3f3f3f3f;
 10 const int maxn=2e4+10;
 11 const int maxm=1e7+10;
 12 const int mod=3;
 13 
 14 int head[maxn<<1],tot;
 15 int root,allnode,n,m,k;
 16 int vis[maxn],dis[maxn],siz[maxn],maxv[maxn];//maxv为重心节点
 17 int ans[maxn];
 18 ll sum=0;
 19 
 20 //namespace IO{
 21 //    char buf[1<<15],*S,*T;
 22 //    inline char gc(){
 23 //        if (S==T){
 24 //            T=(S=buf)+fread(buf,1,1<<15,stdin);
 25 //            if (S==T)return EOF;
 26 //        }
 27 //        return *S++;
 28 //    }
 29 //    inline int read(){
 30 //        int x; bool f; char c;
 31 //        for(f=0;(c=gc())<'0'||c>'9';f=c=='-');
 32 //        for(x=c^'0';(c=gc())>='0'&&c<='9';x=(x<<3)+(x<<1)+(c^'0'));
 33 //        return f?-x:x;
 34 //    }
 35 //    inline long long readll(){
 36 //        long long x;bool f;char c;
 37 //        for(f=0;(c=gc())<'0'||c>'9';f=c=='-');
 38 //        for(x=c^'0';(c=gc())>='0'&&c<='9';x=(x<<3)+(x<<1)+(c^'0'));
 39 //        return f?-x:x;
 40 //    }
 41 //}
 42 //using IO::read;
 43 //using IO::readll;
 44 
 45 namespace FastIO {//读入挂
 46     const int SIZE = 1 << 16;
 47     char buf[SIZE], obuf[SIZE], str[60];
 48     int bi = SIZE, bn = SIZE, opt;
 49     int read(char *s) {
 50         while (bn) {
 51             for (; bi < bn && buf[bi] <= ' '; bi++);
 52             if (bi < bn) break;
 53             bn = fread(buf, 1, SIZE, stdin);
 54             bi = 0;
 55         }
 56         int sn = 0;
 57         while (bn) {
 58             for (; bi < bn && buf[bi] > ' '; bi++) s[sn++] = buf[bi];
 59             if (bi < bn) break;
 60             bn = fread(buf, 1, SIZE, stdin);
 61             bi = 0;
 62         }
 63         s[sn] = 0;
 64         return sn;
 65     }
 66     bool read(int& x) {
 67         int n = read(str), bf;
 68 
 69         if (!n) return 0;
 70         int i = 0; if (str[i] == '-') bf = -1, i++; else bf = 1;
 71         for (x = 0; i < n; i++) x = x * 10 + str[i] - '0';
 72         if (bf < 0) x = -x;
 73         return 1;
 74     }
 75 };
 76 
 77 ll gcd(ll a,ll b)
 78 {
 79     return b==0?a:gcd(b,a%b);
 80 }
 81 
 82 struct node{
 83     int to,next,val;
 84 }edge[maxn<<1];
 85 
 86 void add(int u,int v,int w)//前向星存图
 87 {
 88     edge[tot].to=v;
 89     edge[tot].next=head[u];
 90     edge[tot].val=w;
 91     head[u]=tot++;
 92 }
 93 
 94 void init()//初始化
 95 {
 96     memset(head,-1,sizeof head);
 97     memset(vis,0,sizeof vis);
 98     tot=0;
 99 }
100 
101 void get_root(int u,int father)//求重心
102 {
103     siz[u]=1;maxv[u]=0;
104     for(int i=head[u];~i;i=edge[i].next){
105         int v=edge[i].to;
106         if(v==father||vis[v]) continue;
107         get_root(v,u);
108         siz[u]+=siz[v];
109         maxv[u]=max(maxv[u],siz[v]);
110     }
111     maxv[u]=max(maxv[u],allnode-siz[u]);//保存节点size
112     if(maxv[u]<maxv[root]) root=u;//更新保存当前子树的重心
113 }
114 
115 void get_dis(int u,int father)//获取子树所有节点与根的距离
116 {
117     ans[dis[u]%mod]++;//保存数量
118     for(int i=head[u];~i;i=edge[i].next){
119         int v=edge[i].to;
120         if(v==father||vis[v]) continue;
121         int w=edge[i].val;
122         dis[v]=(dis[u]+w)%mod;
123         get_dis(v,u);
124     }
125 }
126 
127 ll cal(int u,int now)//每一棵子树的计算
128 {
129     ans[0]=ans[1]=ans[2]=0;
130     dis[u]=now;
131     get_dis(u,0);
132     ll ret=ans[1]*ans[2]*2+ans[0]*(ans[0]-1)+ans[0];//1的加上2的+0的
133     return ret;
134 }
135 
136 void solve(int u)
137 {
138     sum+=cal(u,0);//所有满足的
139     vis[u]=1;
140     for(int i=head[u];~i;i=edge[i].next){
141         int v=edge[i].to;
142         int w=edge[i].val;
143         if(vis[v]) continue;
144         sum-=cal(v,w);//去掉子树的,容斥思想。
145         allnode=siz[v];
146         root=0;
147         get_root(v,u);
148         solve(root);
149     }
150 }
151 
152 int main()
153 {
154 //    n=read();
155     FI(n);
156     init();
157     for(int i=1;i<n;i++){
158         int u,v,w;
159         FI(u),FI(v),FI(w);
160         add(u,v,w);
161         add(v,u,w);
162     }
163     root=0;allnode=n;maxv[0]=inf;
164     get_root(1,0);
165     solve(root);
166     ll num=n*n;
167     ll GCD=gcd(sum,num);
168     printf("%lld/%lld\n",sum/GCD,num/GCD);
169 }

 

posted @ 2019-10-10 17:20  ZERO-  阅读(313)  评论(0编辑  收藏  举报