1. 程式人生 > >9.27考試 SD_le NOIP模擬題 第三題 建造遊樂場題解

9.27考試 SD_le NOIP模擬題 第三題 建造遊樂場題解

closed sin es2017 com 需要 style pla math spl

技術分享

技術分享

技術分享

  這道題當時沒讀完題時腦部了無數種問法,然而最後還是猝不及防。一開始還以為是結論題,然而死也退不出來,就先去打第二題了。然後在想這道題時,我想到的是這樣的思路(由於當時時間緊迫,尚未完善):

  我每次向圖中增加兩個點,那麽這兩個點對於所有入度為偶數的點是否連接一定是一致的,如果這兩個點相連,那麽如果使他們分別和一個入度為單數的點相連,那麽他們就是新的入度為單數的點,其他情況就不敘述了,太多了。還不是正解。

  由於最後時間太緊迫了,我不得不打暴力去保分,結果還好丟人好丟人的打錯了……

  正解其實還是挺有意思的,不知道怎麽去概括,但個人覺得我之前的想法和他有一絲絲的相似。

  我們可以明確這樣一個性質,假設有n各點,他們無重邊,無自環,但不一定連通,那麽如果我再去加一個點,去讓他聯通,那麽方案一定存在且只有一種於是我們設g[i]為有i各點讓他們滿足無重邊,無自環,聯通的情況數,就是2^C(i-1,2)即每兩個點之間是否聯通的累乘。

  說這個有什麽用呢,我們可以利用容斥原理去算出f[i]即有i個點的歐拉圖的方案。

  f[i]=g[i]-∑(f[j]*g[i-j]*C(i-1,j-1)),右半側即為1號點在有j點的聯通塊內,向i-j的不一定滿足歐拉圖的另一個塊內連邊且這兩個塊之間無連邊的方案數,C(i-1,j-1)即為那些點在一側的方案數,可以證明這樣無重復。

  最後的答案就是f[n]*C(n,2),為什麽會乘以一個C(n,2)呢,因為我們求出來的只是歐拉圖的個數,對於這個圖中的任意兩個點,如果他們連邊,那麽把這條邊拆去就是題目要求的需要連一條邊才是歐拉圖的圖,如果兩個點之間沒連邊,把這條邊連上就是需要拆邊才符合條件的圖。

  順便吐槽一句,為什麽大佬們打完比賽都不打題解啊,想看一看他們考試的心路借鑒一下怎麽那麽難呢?

技術分享
 1 #include<iostream>
 2 #include<cstdlib>
 3 #include<cstdio>
 4 #include<cstring>
 5 #include<queue>
 6 #include<algorithm>
 7 #include<cmath>
 8 #include<map>
 9 #include<set>
10 #define N 2005
11
using namespace std; 12 long long n,p=1000000007; 13 long long c[N][N],f[N],g[N]; 14 void init() 15 { 16 c[1][0]=c[1][1]=1; 17 for(int i=2;i<=N-2;i++) 18 { 19 for(int j=0;j<=i;j++) 20 { 21 c[i][j]=c[i-1][j]+c[i-1][j-1]; 22 c[i][j]%=p; 23 } 24 } 25 } 26 long long ksm(long long a,long long x) 27 { 28 long long ans=1; 29 while(x) 30 { 31 if(x&1) 32 { 33 ans*=a; 34 ans%=p; 35 } 36 a*=a; 37 a%=p; 38 x>>=1; 39 } 40 return ans; 41 } 42 int main() 43 { 44 init(); 45 scanf("%lld",&n); 46 47 for(int i=3;i<=n;i++) 48 { 49 g[i]=ksm(2ll,c[i-1][2]); 50 } 51 g[1]=g[2]=1; 52 f[1]=1; 53 for(int i=3;i<=n;i++) 54 { 55 f[i]=g[i]; 56 for(int j=1;j<i;j++) 57 { 58 f[i]-=(f[j]*g[i-j]%p*c[i-1][j-1]%p)%p; 59 f[i]%=p; 60 if(f[i]<0)f[i]+=p; 61 } 62 } 63 printf("%lld\n",f[n]*c[n][2]%p); 64 return 0; 65 }
View Code

9.27考試 SD_le NOIP模擬題 第三題 建造遊樂場題解