題目:http://acm.hdu.edu.cn/showproblem.php?pid=4658
題意:問一個數n能被拆分成多少種方法,且每一種方法裡數字重復個數不能超過k(等於k)。 五邊形數定理續,結合上一題(hdu4651),先打表,然後把大於k的個數剪掉;
用了五邊形數定理以及生成函數,然而我看懂了生成函數怎麼搞這題卻不知道為啥生成函數是五邊形數形式= =
首先觀察下面的圖片:
很容易我們可以發現用這種方式構造N個五邊形(假設一個點也算一個五邊形),需要點的個數為:
n∗(3n−1)2
接下來我們來看一下數拆分。
提問:將一個正整數N拆成不少於一個數的和,問有多少種方案。
很容易我們可以構造一個多項式:
P(x)=(1+x1+x2+...)(1+x2+x4+...)(1+x3+x6+...)...
=Px(0)x0+Px(1)x1+Px(2)x2+...+Px(n)xn
可以發現N的數拆分的方案數正對應著多項式展開後xn的系數Px(n)
考慮如下等式:
(1+x1+x2+...)=11−x
歐拉函數ϕ(x)的展開式為:
現在我們要計算Px(n),由於1ϕ(x)=P(x),亦即ϕ(x)P(x)=1。
(1−x−x2+x5+...)(Px(0)+Px(1)x+Px(2)x2+Px(3)x3+...)=1
由於對於滿足i(3i−1)2≤n的i的個數不超過(√n)個,於是計算所有Px(n)的復雜度為O(n(√n))
上面我們說明的是不帶限制的數拆分,現在我們給定一個限制:拆分出來的每種數的個數不能大於等於k(這也是本題的要求)。
類似的,我們考慮生成函數:
(1+x1+x2+...+xk−1)(1+x2+x4+...x2(k−1))...=∏i=0∞1−xki1−xi=ϕ(xk)ϕ(x)=ϕ(xk)P(x)1 #include<iostream> 2 #include<cstdio> 3 #define NN 100005 4 #define LL __int64 5 #define mod 1000000007 6 7 using namespace std; 8 LL wu[NN],pa[NN]; 9 void init() 10 { 11 pa[0]=1; 12 pa[1]=1; 13 pa[2]=2; 14 pa[3]=3; 15 LL ca=0; 16 for(LL i=1; i<=100000/2; i++) 17 { 18 wu[ca++]=i*(3*i-1)/2; 19 wu[ca++]=i*(3*i+1)/2; 20 if(wu[ca-1]>100000) break; 21 } 22 for(LL i=4; i<=100000; i++) 23 { 24 pa[i]=(pa[i-1]+pa[i-2])%mod; 25 ca=1; 26 while(wu[2*ca]<=i) 27 { 28 if(ca&1) 29 { 30 pa[i]=(pa[i]-pa[i-wu[2*ca]]); 31 pa[i]=(pa[i]%mod+mod)%mod; 32 if(wu[2*ca+1]<=i) 33 pa[i]=(pa[i]-pa[i-wu[2*ca+1]]),pa[i]=(pa[i]%mod+mod)%mod; 34 } 35 else 36 { 37 pa[i]=(pa[i]+pa[i-wu[2*ca]]); 38 pa[i]=(pa[i]%mod+mod)%mod; 39 if(wu[2*ca+1]<=i) 40 pa[i]=(pa[i]+pa[i-wu[2*ca+1]]),pa[i]=(pa[i]%mod+mod)%mod; 41 } 42 ca++; 43 } 44 } 45 } 46 LL work(int n,int k) 47 { 48 LL ans=pa[n]; 49 LL ca=0; 50 while(k*wu[2*ca]<=n) 51 { 52 if(ca&1) 53 { 54 ans=(ans+pa[n-k*wu[2*ca]]); 55 ans=(ans%mod+mod)%mod; 56 if(k*wu[2*ca+1]<=n) 57 ans=(ans+pa[n-k*wu[2*ca+1]]),ans=(ans%mod+mod)%mod; 58 } 59 else 60 { 61 ans=(ans-pa[n-k*wu[2*ca]]); 62 ans=(ans%mod+mod)%mod; 63 if(k*wu[2*ca+1]<=n) 64 ans=(ans-pa[n-k*wu[2*ca+1]]),ans=(ans%mod+mod)%mod; 65 } 66 ca++; 67 } 68 return ans; 69 } 70 int main() 71 { 72 int T,n,k; 73 init(); 74 scanf("%d",&T); 75 while(T--) 76 { 77 scanf("%d%d",&n,&k); 78 printf("%I64d\n",work(n,k)); 79 } 80 return 0; 81 82 }
數論還有很多需要學習!