母函数专题

Posted 萌萌的美男子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了母函数专题相关的知识,希望对你有一定的参考价值。

1、Hdu 2082 找单词

  题意:单词A-Z具有1-26的价值,现有字母A-Z的个数num[i],求问在不超过价值为五十的情况下,有多少种字母的组合数.

  思路:用指数代表价值,价值又为数组的下标;用系数代表组成该价值的方案数,方案数为数组中存的值。

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 int re[55];
 5 int p1[55];
 6 int p2[55];
 7 int xx[27];
 8 int main()
 9 {
10     int t;
11     scanf("%d", &t);
12     while (t--)
13     {
14         for (int i = 1; i <= 26; i++) scanf("%d", &xx[i]);
15         for (int i = 0; i <= 50; i++)
16         {
17             if (i <= xx[1]) p1[i]= 1;
18             else p1[i] =0;
19             re[i]=p2[i] = 0;
20         }
21         
22         for (int i = 2; i <= 26; i++)
23         {
24             for (int k = 0; k <= xx[i] && k*i <= 50; k++) p2[k*i] = 1;
25             for (int j = 0; j <=50; j++)
26             {
27                 for (int k = 0; k <= xx[i]; k++)
28                 {//第i个字符的多项式,每项的指数为i*k,系数为1
29                     int zhishu = i*k+j;
30                     if (zhishu > 50) continue;
31                     re[zhishu] += p1[j]*p2[k*i];
32                 }
33             }
34             for (int j = 0; j <= 50; j++) p1[j]=re[j],re[j]=p2[j]=0;
35         }
36         int sum = 0;
37         for (int i = 1; i <= 50; i++) sum += p1[i];
38         printf("%d\n", sum);
39     }
40     return 0;
41 }
View Code

2、HDU 1028 Ignatius and the Princess III

  题意:对给出的数进行拆分,求有多少种拆分方式。

  思路:对于给出的n,其拆成1最多有n个,拆成2最多有n/2个,…,拆成n最多有1个。然后进行n个多项式相乘,最后指数为n的项的系数即为答案。

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 const int maxn = 130;
 5 int re[maxn];
 6 int p1[maxn];
 7 int p2[maxn];
 8 int xx[maxn];
 9 int main()
10 {
11     int n;
12     while (~scanf("%d",&n))
13     {
14         for (int i = 1; i <= n; i++)
15         {
16             xx[i] = n / i;
17         }
18         for (int i = 0; i <maxn; i++)
19         {
20             if (i <= xx[1]) p1[i] = 1;
21             else p1[i] = 0;
22             re[i] = p2[i] = 0;
23         }
24 
25         for (int i = 2; i <= n; i++)
26         {
27             for (int k = 0; k <= xx[i] && k*i <maxn; k++) p2[k*i] = 1;
28             for (int j = 0; j <maxn; j++)
29             {
30                 for (int k = 0; k <= xx[i]; k++)
31                 {//第i个字符的多项式,每项的指数为i*k,系数为1
32                     int zhishu = i*k + j;
33                     if (zhishu >=maxn) continue;
34                     re[zhishu] += p1[j] * p2[k*i];
35                 }
36             }
37             for (int j = 0; j <maxn; j++) p1[j] = re[j], re[j] = p2[j] = 0;
38         }
39         printf("%d\n", p1[n]);
40     }
41     return 0;
42 }
View Code

3、hdu1059 Dividing

  题意:有num[1]个1,num[2]个2......num[6]个6,让你判断它们总和能否均分。

  思路:因为总和过大,直接进行多项式相乘会超时。  

        如果一个数的个数为偶数,那么其显然能够均分。取一个适合的偶数,让1~6各个数字的个数对其取余,取余后的结果乘以各自的值得到sum,然后我们判断sum是否偶数,如果为奇数则说明无法均分。否则,进行多项式相乘,看最后结果中指数为sum/2的系数是否为0.

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 const int maxn = 120010;
 5 int re[maxn];
 6 int p1[maxn];
 7 int p2[maxn];
 8 int xx[7];
 9 int main()
10 {
11     int Case = 1;
12     while (~scanf("%d%d%d%d%d%d", &xx[1],&xx[2],&xx[3],&xx[4],&xx[5],&xx[6]))
13     {
14         int count = xx[1]%60 + 2 * (xx[2]%60) + 3 *(xx[3]%60) + 4 * (xx[4]%60) + 5 * (xx[5]%60) + 6 * (xx[6]%60);//对1~6这6个数的最小公倍数取余(也可以选择其他偶数)
15         if (count == 0) break;
16         if (count % 2)
17         {
18             printf("Collection #%d:\nCan‘t be divided.\n\n", Case++);
19             continue;
20         }
21         count /= 2;
22         memset(re, 0, sizeof(re));
23         memset(p1, 0, sizeof(p1));
24         p1[0] = 1;
25         for (int i = 0; i <= xx[1]&&i<=count; i++) p1[i] = 1;
26         for (int i = 2; i <= 6; i++)
27         {
28             for (int j = 0; j <=count; j++)
29             {
30                 for (int k = 0; k <= xx[i]&&(i*k + j<=count); k++)
31                 {//第i个字符的多项式,每项的指数为i*k,系数为1
32                     
33                     re[i*k + j] += p1[j];
34                 }
35             }
36             memcpy(p1, re, sizeof(re));
37             memset(re, 0, sizeof(re));
38         }
39         if (p1[count]) printf("Collection #%d:\nCan be divided.\n\n", Case++);
40         else printf("Collection #%d:\nCan‘t be divided.\n\n", Case++);
41     }
42     return 0;
43 }
View Code

4、HDU 1171 Big Event in HDU

  题意:有n种物品,第一种物品的单价为v1,数量为m1;第二种物品的单价为v2,数量为m2.....现在要你将这些物品分为两堆,使得这两堆物品的价值尽量接近,输出两堆物品的价值。

  思路:直接进行多项式相乘即可。记总价值为sum,最后从指数为sum/2向前找第一个系数不为0的项。

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 const int maxv = 125010;
 5 int re[maxv];
 6 int p1[maxv];
 7 int p2[maxv];
 8 int xx[55];
 9 int main()
10 {
11     int n;
12     while (~scanf("%d",&n))
13     {
14         if (n < 0) break;
15         int total = 0;
16         memset(xx, 0, sizeof(xx));
17         for (int i = 1; i <= n; i++)
18         {
19             int v, w;
20             scanf("%d%d", &v, &w);
21             xx[v] += w;
22             total += v*w;
23         }
24         int totalv= total/2;
25         int st;
26         for (st = 0;xx[st]==0; st++);
27         memset(re, 0, sizeof(re));
28         memset(p1, 0, sizeof(p1));
29         memset(p2, 0, sizeof(p2));
30 
31         for (int i = 0; i <= totalv; i+=st)
32         {
33             p1[i] = 1;
34         }
35         for (int i = 2; i <= 50; i++)
36         {
37             if (xx[i] == 0) continue;
38             for (int k = 0; k <= xx[i] && k*i <=totalv; k++) p2[k*i] = 1;
39             for (int j = 0; j <= totalv; j++)
40             {
41                 for (int k = 0; k <= xx[i]&& i*k + j<=totalv; k++)
42                 {//第i个字符的多项式,每项的指数为i*k,系数为1
43                     re[i*k + j] += p1[j] * p2[k*i];
44                 }
45             }
46             memcpy(p1, re, sizeof(re));
47             memset(re, 0, sizeof(re));
48             memset(p2, 0, sizeof(p2));
49         }
50         int ans = totalv;
51         while (p1[ans] == 0) ans--;
52         printf("%d %d\n", total - ans, ans);
53     }
54     return 0;
55 }
View Code

5、HDU 1709 The Balance

  题意:给定n个砝码的重量,总质量为sum,问在1~sum中有多少个重量不能被称出来。

  思路:记下各个砝码的价值,做n次多项式乘法。  由于砝码可以两边放,所以母函数做完后做一次差值,如果指数为i和指数为j(i≤j)项系数不为0,说明可以称量出重量为i,j,也可以称出j-i。

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 const int maxv = 10010;
 5 int re[maxv];
 6 int p1[maxv];
 7 int p2[maxv];
 8 int xx[maxv];
 9 int num[maxv];
10 int main()
11 {
12     int n;
13     while (~scanf("%d", &n))
14     {
15         int total = 0;
16         memset(xx, 0, sizeof(xx));
17         for (int i = 1; i <= n; i++)
18         {
19             scanf("%d", &xx[i]);
20             total += xx[i];
21         }
22         memset(re, 0, sizeof(re));
23         memset(p1, 0, sizeof(p1));
24         memset(p2, 0, sizeof(p2));
25         for (int i = 0;i<=1;i++)
26         {
27             p1[i*xx[1]] = 1;
28         }
29         for (int i = 2; i <= n; i++)
30         {
31             for (int k = 0; k <= 1;k++) p2[k*xx[i]] = 1;
32             for (int j = 0; j <= total; j++)
33             {
34                 for (int k = 0;k<=1&&xx[i]*k+j<=total; k++)
35                 {//第i个字符的多项式,每项的指数为i*k,系数为1
36                     re[xx[i]*k + j] += p1[j] * p2[k*xx[i]];
37                 }
38             }
39             memcpy(p1, re, sizeof(re));
40             memset(re, 0, sizeof(re));
41             memset(p2, 0, sizeof(p2));
42         }
43         for (int i = 0; i <= total; i++)
44         {
45             for (int j = i + 1; j <= total; j++)
46             {
47                 if (p1[i] && p1[j])re[j - i] = 1;
48             }
49         }
50         int ans = 0;
51         for (int i = 1; i <= total; i++) if (re[i] == 0) ans++;
52         printf("%d\n", ans);
53         if (ans > 0)
54         {
55             bool fst = true;
56             for (int i = 1; i <= total; i++)
57             {
58                 if (re[i] == 0)
59                 {
60                     if (fst)
61                     {
62                         printf("%d", i);
63                         fst = false;
64                     }
65                     else printf(" %d", i);
66                 }
67             }
68             printf("\n");
69         }
70     }
71     return 0;
72 }
View Code

6、HDU 1398 Square Coins

  题意:用价值为1,4,9,16,…,289(1~17的平方数)的硬币,组成价值为n的方案数。

  思路:由于n最大不超过300,我们可以先进行一次母函数(用300对各种硬币的价值做除得到数目,然后做17次多项式乘法),然后对每次查询输出其对应指数的系数即可。

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 const int maxn = 20;
 5 const int maxv = 400;
 6 int wp[maxn];
 7 int re[maxv];
 8 int tmp[maxv];
 9 int main()
10 {
11     for (int i = 1; i <= 17; i++)
12     {
13         wp[i] = 300 / (i*i);
14     }
15     for (int i = 0; i <= wp[1]; i++)
16     {
17         re[i] = 1*1*1;
18     }
19     for (int i = 2; i <= 17; i++)
20     {
21         for (int j = 0; j <= 300; j++)
22         {
23             for (int k = 0; k <= wp[i] && k*i*i + j <= 300; k++)
24             {
25                 tmp[k*i*i + j] += re[j]*1;
26             }
27         }
28         memcpy(re, tmp, sizeof(tmp));
29         memset(tmp, 0, sizeof(tmp));
30     }
31     int n;
32     while (~scanf("%d", &n)&&n)
33     {
34         printf("%d\n", re[n]);
35     }
36     return 0;
37 }
View Code

7、HDU-1085 Holding Bin-Laden Captive

  题意:给你三种不同价值的硬币,分别为1、2、5,告诉你各自的数量,求由这些钱币所不能组成的最小价值。

  思路:对3个多项式做乘,然后从常数项往后找第一个系数为0的项。

技术分享
 1 #include<iostream>
 2 #include<memory.h>
 3 using namespace std;
 4 const int maxv = 8005;
 5 int re[maxv];
 6 int tmp[maxv];
 7 int main()
 8 {
 9     int n1, n2, n5;
10     while (~scanf("%d%d%d", &n1, &n2, &n5))
11     {
12         if (n1 == 0 && n2 == 0 && n5 == 0) break;
13         int tsum = n1 +n2*2 + n5* 5;
14         memset(re, 0, sizeof(re));
15         memset(tmp, 0, sizeof(tmp));
16         for (int i = 0; i <= n1&&i * 1 <= tsum; i++)
17         {
18             re[i*1] = 1;
19         }
20         for (int i = 2; i <= 3; i++)
21         {
22             int v = (i == 2 ? 2 : 5);
23             for (int j = 0; j <= tsum; j++)
24             {
25                 int num = (i == 2 ? n2 : n5);
26                 for (int k = 0; k <= num&&k*v + j <= tsum; k++)
27                 {
28                     tmp[k*v + j] += re[j];
29                 }
30             }
31             memcpy(re, tmp, sizeof(tmp));
32             memset(tmp, 0, sizeof(tmp));
33         }
34         int pos = 0;
35         while (pos<=tsum&&re[pos] != 0)pos++;
36         printf("%d\n", pos);
37     }
38     return 0;
39 }
View Code

8、HDU 2110 Crisis of HDU

  题意:公司此时一共有n种价值的资产,每种价值的资产数量已知,求分得到总价值1/3的方案数。

  思路:如果总价值不能整除3,则方案数为0;否则,进行n-1次多项式乘法,看指数为sum/3的系数。

技术分享
 1 #include<iostream>
 2 using namespace std;
 3 struct sny
 4 {
 5     int p;//价值
 6     int m;//数量
 7 }ss[110];
 8 long long re[10005];
 9 long long tmp[10005];
10 int main()
11 {
12     int n;
13     while (~scanf("%d", &n) && n)
14     {
15         int sum = 0;
16         for (int i = 1; i <= n; i++)
17         {
18             scanf("%d%d", &ss[i].p, &ss[i].m);
19             sum += ss[i].p*ss[i].m;
20         }
21         if (sum % 3)
22         {
23             printf("sorry\n");
24             continue;
25         }
26         memset(re, 0, sizeof(re));
27         memset(tmp, 0, sizeof(tmp));
28         for (int i = 0; i <= ss[1].m&&i*ss[1].p <= sum; i++)
29         {
30             re[i*ss[1].p] = 1;
31         }
32         for (int i = 2; i <= n; i++)
33         {
34             int count = ss[i].m, val = ss[i].p;
35             for (int j = 0; j <= sum; j++)
36             {
37                 for (int k = 0; k <= count&&k*val + j <= sum; k++)
38                 {
39                     tmp[k*val + j] =(tmp[k*val + j]+ re[j])%10000;
40                 }
41             }
42             memcpy(re, tmp, sizeof(tmp));
43             memset(tmp, 0, sizeof(tmp));
44         }
45         if(re[sum/3]>0)printf("%lld\n", re[sum / 3] % 10000);
46         else printf("sorry\n");
47     }
48     return 0;
49 }
View Code

 9、hdu 1521 排列组合

  题意:有n种物品,并且知道每种物品的数量。要求从中选出m件物品的排列数。例如有两种物品A,B,并且数量都是1,从中选2件物品,则排列有"AB","BA"两种。

  思路:指数型母函数。

技术分享
 1 #include<iostream>
 2 #include<cstdio>
 3 #include<cstring>
 4 using namespace std;
 5 long long Cal(int a)
 6 {
 7     long long ans = 1;
 8     for (int i = 1; i <= a; i++) ans = ans*i;
 9     return ans;
10 }
11 const int maxn = 1010;
12 int num[12];
13 double p1[maxn];
14 double p2[maxn];
15 int main()
16 {
17     int n, m;
18     while (~scanf("%d%d", &n, &m))
19     {
20         for (int i = 1; i <= n; i++) scanf("%d", &num[i]);
21         memset(p2, 0, sizeof(p2));
22         memset(p1, 0, sizeof(p1));
23 
24         for (int i = 0; i <= num[1]; i++)
25         {
26             p1[i] = 1.0 / Cal(i);
27         }
28         for (int i = 2; i <= n; i++)
29         {
30             for (int k = 0; k <= m; k++)
31             {
32                 for (int j = 0; j <= num[i] && j + k <= m; j++)
33                 {
34                     p2[j + k] += p1[k] / Cal(j);
35                 }
36             }
37             for (int j = 0; j <= m; j++)
38             {
39                 p1[j] = p2[j];
40                 p2[j] = 0;
41             }
42         }
43         printf("%.0lf\n", p1[m] * Cal(m));
44     }
45     return 0;
46 }
View Code

 10、hdu 2065 红色病毒(指数型母函数+泰勒级数(麦克劳林展式)+非递归快速幂)

  题意:

现在有一长度为N的字符串,满足一下条件:
(1) 字符串仅由A,B,C,D四个字母组成;
(2) A出现偶数次(也可以不出现);
(3) C出现偶数次(也可以不出现);
计算满足条件的字符串个数.
当N=2时,所有满足条件的字符串有如下6个:BB,BD,DB,DD,AA,CC.
由于这个数据肯能非常庞大,你只要给出最后两位数字即可.

  思路:

由4种字母组成,A和C只能出现偶数次。

构造指数型母函数:(1+x/1!+x^2/2!+x^3/3!……)^2*(1+x^2/2!+x^4/4!+x^6/6!……)^2.

前面是B和D的情况,可以任意取,但是相同字母一样,所以要除去排列数。后者是A和C的情况,只能取偶数个情况。

根据泰勒展开,e^x在x0=0点的n阶泰勒多项式为 1+x/1!+x^2/2!+x^3/3!……

而后者也可以进行调整,需要把奇数项去掉,则e^(-x)的展开式为1-x/1!+X^2/2!-X^3/3!……

所以后者可以化简为(e^x+e^(-x))/2。则原式为 (e^x)^2   *  ((e^x*e^(-x))/2)^2

整理得到e^4x+2*e^2x+1。

又由上面的泰勒展开式得到

e^4x = 1 + (4x)/1! + (4x)^2/2! + (4x)^3/3! + ... + (4x)^n/n!;

e^2x = 1 + (2x)/1! + (2x)^2/2! + (2x)^3/3! + ... + (2x)^n/n!;

对于系数为n的系数为(4^n+2*2^n)/4=4^(n-1)+2^(n-1),记为答案。

技术分享
 1 //f(x)=(1+x/1!+x^2/2!+x^3/3!…+x^n/n!)^2+(1+x^2/2!+x^4/4!+x^6/6!…+…)^2
 2 //e^x=1+x/1!+x^2/2!+x^3/3!…+x^n/n!;e^(-x)=1-x/1!+x^2/2!-x^3/3!+…-…
 3 //e^x+e^(-x)=1+x^2/2!+x^4/4!+x^6/6!…+…
 4 // f(x)=e^(2x) * ((e^x+e^(-x))/2)^2
 5 //a(n)=(4^(n-1) + 2^(n-1)) * x^n/n!
 6 //F(n) = (4 ^ (n - 1) + 2 ^ (n - 1)) % 100.
 7 #include<iostream>
 8 using namespace std;
 9 long long quick_pow(long long a, long long b, long long md)
10 {
11     long long sum = 1;
12     while (b)
13     {
14         if (b & 1) sum = (sum*a) % md;
15         b >>= 1;
16         a = (a*a) % md;
17     }
18     return sum;
19 }
20 int main()
21 {
22     int t;
23     long long n;
24     while (~scanf("%d", &t) && t)
25     {
26         int Case = 1;
27         while (t--)
28         {
29             scanf("%lld", &n);
30             int sum = (quick_pow(4, n - 1, 100) + quick_pow(2, n - 1, 100)) % 100;
31             printf("Case %d: %d\n", Case++, sum);
32         }
33         printf("\n");
34     }
35     return 0;
36 }
View Code

 

以上是关于母函数专题的主要内容,如果未能解决你的问题,请参考以下文章

HDU1171母函数

母函数

HDU1398物品无限的母函数

2079 ACM 选课时间 背包 或 母函数

hdu1398 普通母函数的应用 解决多重集组合问题

转 母函数