汕頭市隊賽 SRM10 T3 數學上來先打表
阿新 • • 發佈:2017-08-06
一行 algo small ret 費馬小定理 fine space lld int
數學上來先打表 SRM 10
描述
給出 n個點(不同點之間有區別),求出滿足下列條件的連邊(雙向邊)方案:
1.每條邊連接兩個不同的點,每兩個點之間至多有一條邊
2.不存在三個點a,b,c使三個點間兩兩可以互相到達且兩兩之間最短距離相等
3.邊的長度均為1
輸入格式
一行,一個整數n
輸出格式
一行,一個整數,表示方案數對1004535809取模的結果。
樣例輸入
3
樣例輸出
7
數據範圍與約定
對於8組數據,1<=n<=9
對於余下8組數據,10<=n<=2000
樣例解釋
三個點之間沒有邊,有1種方案
三個點之間有一條邊,有3種方案
三個點之間有兩條邊,有3種方案
—————————————————————
這道題觀察可得這n個數組成的不是環就是鏈
是環還必須大於3且不能是3的倍數
我們任然可以先預處理出各個階乘的逆元
T是2關於mod的逆元 這個根據費馬小定理可以算出來
f【i】表示大小為i的聯通塊的個數
如果是鏈 方案數是 i!/2 這個時候T作為2的逆元就顯示出作用辣
至於為什麽是i!/2 因為各個點不同我們可以看作是排列而一個排列對應一個方案,一個方案被算兩次
如果是環 就是(i-1 )!/2 因為是環我們可以欽定一個數作為起點 剩下i-1個數來排列
當然只有1不符合上述推斷 所以要單獨考慮
這樣處理完之後我們就可以來算答案ans辣
ans【k】=sigma(1-k) f【i】*g【k-i】*C(i-1,j-1);
也就是我們選i個數作為聯通快其余隨意的總和
至於為什麽是C(i-1,j-1)
因為如果固定一個點統計,g就可以每個圖只算一次
這樣才能保證每個合法方案中只有一個連通塊被算到,且只算了一次
#include<cstdio> #include<cstring> #include<algorithm> #define LL long long using namespace std; const int M=2007,mod=1004535809; LL read(){ LL ans=0,f=1,c=getchar(); while(c<‘0‘||c>‘View Code9‘){if(c==‘-‘) f=-1; c=getchar();} while(c>=‘0‘&&c<=‘9‘){ans=ans*10+(c-‘0‘); c=getchar();} return ans*f; } int n; LL T,w[M],b[M],f[M],ans[M]; LL qmod(LL a,LL b,LL c){ LL ans=1; while(b){ if(b&1) ans=ans*a%c; b=b/2; a=a*a%c; } return ans; } void prepare(){ int mx=2000; w[0]=1; for(int i=1;i<=mx;i++) w[i]=w[i-1]*i%mod; b[mx]=qmod(w[mx],mod-2,mod); for(int i=mx;i>=1;i--) b[i-1]=b[i]*i%mod; T=qmod(2,mod-2,mod); } LL C(int n,int m){return w[n]*b[m]%mod*b[n-m]%mod;} int main() { n=read(); prepare(); f[1]=1; for(int i=2;i<=n;i++){ f[i]=w[i]*T%mod; if(i>3&&i%3) f[i]=(f[i]+w[i-1]*T)%mod; } ans[0]=1; for(int i=1;i<=n;i++){ for(int j=1;j<=i;j++){ ans[i]=(ans[i]+f[j]*ans[i-j]%mod*C(i-1,j-1)%mod)%mod; } } printf("%lld\n",ans[n]); return 0; }
汕頭市隊賽 SRM10 T3 數學上來先打表