• 母函数专题


    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
    ", 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
    ", 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:
    Can't be divided.
    
    ", 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:
    Can be divided.
    
    ", Case++);
    40         else printf("Collection #%d:
    Can't be divided.
    
    ", 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
    ", 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
    ", 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("
    ");
    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
    ", 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
    ", 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
    ");
    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
    ", re[sum / 3] % 10000);
    46         else printf("sorry
    ");
    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
    ", 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
    ", Case++, sum);
    32         }
    33         printf("
    ");
    34     }
    35     return 0;
    36 }
    View Code
  • 相关阅读:
    利用单臂路由实现VLAN间路由
    理解Hybrid接口的应用
    配置Trunk接口
    VLAN 基础设置及Aceess接口
    NFS实现文件共享
    Apache服务(基于IP地址,主机名,端口号)
    使用Vsftpd服务(匿名访问模式、本地用户模式)
    移动端响应式js代码
    快速搜索匹配插件
    无缝滚动插件
  • 原文地址:https://www.cnblogs.com/ivan-count/p/7414423.html
Copyright © 2020-2023  润新知