1. 程式人生 > >原創題目 拼方頭 【組合數+記憶化優化】

原創題目 拼方頭 【組合數+記憶化優化】

其他 stdout 大於 longest 分享 isp 技術 nbsp his

題目:

有 n 條木棒,現在從中選 x 根,想要組成一個正 x-1 邊形,問有幾
種選法?
由於答案較大,輸出它 mod 19260817 的答案。
萬古神犇 hjr 秒了這道題,現在交給你做,好讓方頭開心一下。

分析:

此題有兩個突破點:

1、既然是用x根木棒去拼x-1邊形,那麽必然有且僅有一條邊是由兩根木棒拼成的;

2、且其他邊必然是由相同的木棒拼成的,也就是說,一種木棒僅當邊數大於x-2時,才有可能成為正多邊形中的邊長;

於是我們可以一 一枚舉滿足(2)的木棒,並再通過枚舉拼湊第x-1條邊;(用cnt[i]表示長度為i的邊有多少根)

首先,從cnt[i]根滿足(2)的木棒中選出x-2根去拼那x-2條邊,有C(cnt[i],x-2)種選擇;-------?

其次,假設a+b=i,那麽用a和b去拼第x-1條邊,就有cnt[a]*cnt[b]種拼法;但若是a=b,就是C(cnt[a],2);-----------?

顯然,?和?是符合乘法原理的,因此就有:f[i]=C(cnt[i],x-2)*cnt[a]*cnt[b]或是C(cnt[i],x-2)*C(cnt[a],2);

下面是參考代碼:

技術分享
#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const
long long mod=19260817; long long nn,x,a,maxx=0; long long cnt[1550]={0},p[1550],ex[1550]; long long f[1550]={0}; bool ok[1550]; long long C(long long n,long long m) { if(n<m) return 0; if(n==m) return 1; if(m==1) return n%mod; long long c=1; for(long long i=1;i<=m;i++) c=(c*(n-i+1
)/i)%mod; return c; } long long cal(long long i) { if(ex[i]) return ex[i];//memorizing search memset(p,0,sizeof(p)); long long an=0; for(long long j=1;j<=min(maxx,i);j++) { if(p[j]) return an;//avoid repeated calculating if(j!=i-j) { an=(an+cnt[j]*cnt[i-j])%mod; p[j]=p[i-j]=1; } else { an=(an+C(cnt[j],2))%mod; p[j]=1; } } return ex[i]=an; } int main() { // freopen("ft.in","r",stdin); // freopen("ft.out","w",stdout); cin>>nn>>x; for(int i=1;i<=nn;i++) { cin>>a; maxx=max(maxx,a); cnt[a]++; if(cnt[a]>=x-2) ok[a]=1; } for(int i=1;i<=maxx;i++) { if(ok[i])//if it is possible for this edge to be the longest edge { long long a1=C(cnt[i],x-2); long long a2=cal(i); f[i]=(a1*a2)%mod; //pick x-2 out of cnt[i] for the longest edge //how many couple can make up i //multiply both above } } long long ans=0; for(long long i=1;i<=maxx;i++) ans=(ans+f[i])%mod; cout<<ans; return 0; }
View Code

原創題目 拼方頭 【組合數+記憶化優化】