洛谷 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 }