洛谷 P2634 [國家集訓隊]聰聰可可-樹分治(點分治,容斥版) +讀入掛+手動O2優化吸點氧才過。。。-樹上路徑爲3的倍數的路徑數量

P2634 [國家集訓隊]聰聰可可

題目描述

聰聰和可但是兄弟倆,他們倆常常爲了一些雜事打起來,例如家中只剩下最後一根冰棍而兩人都想吃、兩我的都想玩兒電腦(但是他們家只有一臺電腦)……遇到這種問題,通常狀況下石頭剪刀布就行了,但是他們已經玩兒膩了這種低智商的遊戲。node

他們的爸爸快被他們的爭吵煩死了,因此他發明了一個新遊戲:由爸爸在紙上畫n個「點」,並用n-1條「邊」把這n個「點」剛好連通(其實這就是一棵樹)。而且每條「邊」上都有一個數。接下來由聰聰和可可分別隨即選一個點(固然他們選點時是看不到這棵樹的),若是兩個點之間全部邊上數的和加起來剛好是3的倍數,則判聰聰贏,不然可可贏。c++

聰聰很是愛思考問題,在每次遊戲後都會仔細研究這棵樹,但願知道對於這張圖本身的獲勝機率是多少。現請你幫忙求出這個值以驗證聰聰的答案是否正確。網絡

輸入格式

輸入的第1行包含1個正整數n。後面n-1行,每行3個整數x、y、w,表示x號點和y號點之間有一條邊,上面的數是w。優化

輸出格式

以即約分數形式輸出這個機率(即「a/b」的形式,其中a和b必須互質。若是機率爲1,輸出「1/1」)。spa

輸入輸出樣例

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

說明/提示

【樣例說明】code

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)。orm

【數據規模】blog

對於100%的數據,n<=20000。遊戲

 

題意就是找樹上任意兩點路徑爲3的倍數的路徑數,除以全部路徑數。get

點分治,最後兩組數據怎麼也過不了,今天過了。

換了個讀入掛,也不行,手動吸氧過了。

不知道爲何總是超時,有點躁。

 

關於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 }
相關文章
相關標籤/搜索