总结的不错的博客 https://www.cnblogs.com/icodefive/p/4578530.html
重要公式
图片来源 https://blog.csdn.net/qq_41357771/article/details/83449481
来源于百度
(一)普通型母函数
1. 经典问题 我有1元 2元 5元的硬币无限个 我如果要组成n元 有多少种方案
(1) 解决方法很多
(2) 母函数解决方法 构建母函数 (1+x+x2+x3+....+xn)*(1+x2+x4+x6+....+x2n)*(1+x5+x10+x15+...+x5n)
求出这个多项式 a0+a1x1 +a2x2+.......anxn 拼成 n元的方案就是an个
2. hdu 1028 (和上述问题差不多)
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=1e2+50; int a[maxn][maxn]; int ans[maxn]; int main(){ int n; while(~scanf("%d",&n)){ for(int i=1;i<=n;i++){ for(int j=0;j<=n;j+=i){ a[i][j]=1; } } for(int i=1;i<=n;i++){ if(i==1){ for(int j=0;j<=n;j++){ ans[j]=a[i][j]; } } else { for(int j=0;j<=n;j++){ a[i-1][j]=ans[j]; ans[j]=0; } for(int j=0;j<=n;j++){ for(int k=0;k<=n;k++){ if(j+k>n) continue; ans[j+k]+=a[i-1][j]*a[i][k]; } } } } printf("%d\n",ans[n]); for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++) a[i][j]=0; ans[i]=0; } } } hdu 1028
3. hdu 1085 (暴力)
#include<bits/stdc++.h> using namespace std; const int maxn=8000+10; int a[maxn]; int b[maxn]; int c[maxn]; int ans[maxn]; int num[maxn]; int main(){ int x,y,z; while(cin>>x>>y>>z){ if(x==0 &&y==0 && z==0){break; } int n=1*x+2*y+5*z; //cout<<n<<endl; for(int i=0;i<=x;i++) a[i]=1; for(int i=0;i<=y;i++) b[2*i]=1; for(int i=0;i<=z;i++) c[5*i]=1; for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ if(i+j>n) continue; ans[i+j]+=a[i]*b[j]; } } for(int i=0;i<=n;i++){ for(int j=0;j<=n;j++){ if(i+j>n) continue; num[i+j]+=ans[i]*c[j]; } } int k=n+1; for(int i=1;i<=n;i++){ if(num[i]==0) k=min(i,k); } cout<<k<<endl; for(int i=0;i<=n;i++){ a[i]=b[i]=c[i]=ans[i]=num[i]=0; } } } hdu 1085
4. hdu 4651 (拆分数) P(n) 拆分数+广义五边形优化 ****注意和贝尔数的区别******
五边形数 (1ll*3*x*x-x)*inv2%mod;
五边形数范围 0-n;
广义五边形数范围 0 1 -1 2 -2 3 -3
p(k) = p(k − 1) + p(k − 2) − p(k − 5) − p(k − 7) + p(k − 12) + p(k − 15) − p(k − 22) − ...
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int mod=1e9+7; const int inv2=(1+mod)/2; int wu[maxn]; // 0, 1, 2, 5, 7, 12, 15, 22, 26, 35.... // i 0, 1, -1, 2, -2, 3, -3, 4, -4 , 5..... int flug[maxn]; int chai[maxn]; // 1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42 ,56,77,101,135...... int five(int x){ return (1ll*3*x*x-x)*inv2%mod; } void init(){ wu[0]=five(0); for(int i=1;i<maxn;i++){ if(i%2==1) wu[i]=five(i/2+1); else wu[i]=five(-i/2); } for(int i=0;i<maxn;i++){ int x=(i+1)/2; if(x%2==1) flug[i]=1; else flug[i]=-1; } //for(int i=0;i<maxn;i++) cout<<wu[i]<<" "; cout<<endl; //for(int i=0;i<maxn;i++) cout<<flug[i]<<" "; cout<<endl; chai[0]=1; for(int i=1;i<maxn;i++){ for(int j=1;wu[j]<=i;j++){ chai[i]+=flug[j]*chai[i-wu[j]]; chai[i]%=mod; } } //for(int i=0;i<20;i++) cout<<chai[i]<<" "; cout<<endl; } int32_t main(){ init(); int T; scanf("%d",&T); while(T--){ int n; scanf("%d",&n); printf("%d\n",(chai[n]%mod+mod)%mod); } } hdu 4651
5 hdu 4658 (拆分数) (条件限制)
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int mod=1e9+7; const int inv2=(1+mod)/2; int wu[maxn]; // 0, 1, 2, 5, 7, 12, 15, 22, 26, 35.... // i 0, 1, -1, 2, -2, 3, -3, 4, -4 , 5..... int flug[maxn]; int chai[maxn]; // 1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42 ,56,77,101,135...... int cmp[maxn]; int five(int x){ return (1ll*3*x*x-x)*inv2%mod; } void init(){ wu[0]=five(0); for(int i=1;i<maxn;i++){ if(i%2==1) wu[i]=five(i/2+1); else wu[i]=five(-i/2); } for(int i=0;i<maxn;i++){ int x=(i+1)/2; if(x%2==1) flug[i]=1; else flug[i]=-1; } //for(int i=0;i<maxn;i++) cout<<wu[i]<<" "; cout<<endl; //for(int i=0;i<maxn;i++) cout<<flug[i]<<" "; cout<<endl; chai[0]=1; for(int i=1;i<maxn;i++){ for(int j=1;wu[j]<=i;j++){ chai[i]+=flug[j]*chai[i-wu[j]]; chai[i]%=mod; } } //for(int i=0;i<20;i++) cout<<chai[i]<<" "; cout<<endl; } int ffind(int n,int k){ int ans=chai[n]; //cout<<n<<" "<<k<<endl; for(int j=1;k*wu[j]<=n;j++){ // cout<<k*wu[j]<<endl; if(j%4==1 || j%4== 2 ) ans-=chai[n-k*wu[j]]; else ans+=chai[n-k*wu[j]]; ans%=mod; } return (ans%mod+mod)%mod; } int32_t main(){ init(); int T; scanf("%d",&T); while(T--){ int n,k; scanf("%d %d",&n,&k); int x=ffind(n,k); printf("%d\n",x); } } hdu 4658
6. hdu 6042
#include<bits/stdc++.h> using namespace std; const int maxn=1e5+10; const int mod=1e9+7; const int inv2=(1+mod)/2; int wu[maxn]; // 0, 1, 2, 5, 7, 12, 15, 22, 26, 35.... //i0, 1, -1, 2, -2, 3, -3, 4, -4 , 5..... int flug[maxn]; int chai[maxn]; // 1, 1, 2, 3, 5, 7, 11, 15, 22, 30, 42 ,56,77,101,135...... int five(int x){ return (1ll*3*x*x-x)*inv2%mod; } void init(){ wu[0]=five(0); for(int i=1;i<maxn;i++){ if(i%2==1) wu[i]=five(i/2+1); else wu[i]=five(-i/2); } for(int i=0;i<maxn;i++){ int x=(i+1)/2; if(x%2==1) flug[i]=1; else flug[i]=-1; } chai[0]=1; for(int i=1;i<maxn;i++){ for(int j=1;wu[j]<=i;j++){ chai[i]+=flug[j]*chai[i-wu[j]]; chai[i]%=mod; } } } int a[maxn]; int b[maxn]; int dp[maxn]; // work- chang shu void work(int n) { // (1-(x^(a[i]*i+i) ) for(int i=0;i<=2*n;i++) dp[i]=chai[i]; for(int i=1;i<=n;i++){ for(int j=2*n;j>=1ll*(a[i]+1)*i;j--){ // n*sqrt(n); dp[j]-=dp[j-(a[i]+1)*i]; dp[j]=(dp[j]%mod+mod)%mod; } } } int num[maxn]; void slove(int n){ num[0]=dp[0]; for(int i=1;i<=n;i++){ num[i]=num[i-1]+dp[i]; num[i]%=mod; } for(int i=n+1;i<=2*n;i++){ dp[i]-=num[i-n-1]; } for(int i=0;i<=2*n;i++) dp[i]=(dp[i]%mod+mod)%mod; } int32_t main(){ init(); int n,m; int cs=0; while(~scanf("%d %d",&n,&m)){ for(int i=1;i<=n;i++) scanf("%d",&a[i]); for(int i=1;i<=m;i++) scanf("%d",&b[i]); work(n); slove(n); int mm=0; for(int i=1;i<=m;i++) {mm+=dp[2*n-b[i]]; mm=mm%mod;} printf("Case #%d: ", ++cs); printf("%d\n",mm); for(int i=0;i<=2*n;i++){ a[i]=b[i]=dp[i]=num[i]=0; } } return 0; } /* 3 2 3 // 2 6 12 3 3 */ hdu 6042 17多校
7 hdu 5689 (母函数+NTT)
#include<bits/stdc++.h> #define ll long long using namespace std; const int mod=998244353; const int maxn=1e6+10; //int a[maxn]; ll b[maxn]; ll c[maxn]; ll d[maxn]; ll WWW[maxn]; ll qpow(ll base,ll k) { ll tmp=1; while(k) { if(k&1) tmp=tmp*base%mod; base=base*base%mod; k>>=1; } return tmp; } void NTT(ll *a,int n,int flag) { for(int i=0,k=0;i<n;++i) { if(i>k) swap(a[i],a[k]); for(int j=(n>>1);(k^=j)<j;j>>=1); } for(int i=1;i<n;i<<=1) { ll wn=qpow(3, (mod-1)/(i<<1)); if(flag==-1) wn=qpow(wn, mod-2); for(int j=0;j<n;j+=(i<<1)) { ll w=1,x,y; for(int k=0;k<i;++k) { x=a[j+k], y=1ll*w*a[j+k+i]%mod; a[j+k]=(1ll*x+y)%mod, a[j+k+i]=(1ll*x-y+mod)%mod; w*=wn, w%=mod; } } } if(flag==-1) { ll rev=qpow(n, mod-2); for(int i=0;i<n;++i) a[i]=(1ll*a[i]*rev)%mod; } } void make(int n,int m,ll *A,ll *B){ int len=0; for(len=1;len<(n+m+1);len<<=1); NTT(A,len,1); NTT(B,len,1); for(int i=0;i<len;++i) A[i]=(1ll*A[i]*B[i])%mod; NTT(A,len,-1); for(int i=n;i<len;++i) A[i] = 0; // NTT(B,len,-1); } const int N=2e6+10; ll xx[N]; ll yy[N]; void inint(){ xx[0]=1; yy[0]=1; int n=N-1; for(int i=1;i<=n;i++) xx[i]=xx[i-1]*i%mod; yy[n]=qpow(xx[n],mod-2); for(int i=n-1;i>=1;i--) yy[i]=yy[i+1]*(i+1)%mod; } int CC(int n,int w){ if(w>n) return 0; return 1ll*xx[n]*yy[w]%mod*yy[n-w]%mod; } int main(){ inint(); //cout<<CC(1000000,5000)<<endl; int T; scanf("%d",&T); while(T--){ int n,m; scanf("%d %d",&n,&m); for(int i=0;i<n;i++) scanf("%lld",&WWW[i]); int x=0,y=0,z=0; for(int i=1;i<=m;i++){ int w; scanf("%d",&w); if(w==1) x++; if(w==2) y++; if(w==3) z++; } if(x) { for(int i=0;i<n;i++) if(i%1==0) { int t=i/1; b[i]=CC(x+t-1,t); } } if(y) { for(int i=0;i<n;i++) if(i%2==0) { int t=i/2; c[i]=CC(y+t-1,t); } } if(z) { for(int i=0;i<n;i++) if(i%3==0) { int t=i/3; d[i]=CC(z+t-1,t); } } if(x) make(n,n,WWW,b); if(y) make(n,n,WWW,c); if(z) make(n,n,WWW,d); ll ans=0; for(int i=0;i<n;i++) { WWW[i]=(WWW[i]%mod+mod)%mod; ans=( ans^( (i+1)*WWW[i]) );} printf("%lld\n",ans); memset(WWW,0,sizeof(WWW)); memset(b,0,sizeof(b)); memset(c,0,sizeof(c)); memset(d,0,sizeof(d)); } return 0; }
(二)指数型母函数
推荐博客:https://blog.csdn.net/SunPeishuai/article/details/81411702
1. 经典问题 有n个位置编号1-n 每个位置可以填1-m种的任意数 (答案m的n次方....别管....)
我们考虑 n=3; m=2;的情况 (太大了写不了)
方案数 (1,1,1)(1 1 2) (1,2,1) (1,2,2) (2,1,1) (2,1,2) (2,2,1) (2,2,1) 8个方案
1出现2次 2出现1次的方案数有3种 计算方式 3!/(2!)/(1!) !表示阶乘
我们就可以构造母函数 (1+x/(1!)+x2/(2!)+x3/(3!)+....+xn/(n!) ) *(1+x/(1!)+x2/(2!)+x3/(3!)+....+xn/(n!) ) *....... (m个多项式相乘)
求出这个多项式 a0+a1x1 +a2x2+.......anxn 拼成 n元的方案就是an * (n!) 个
2.计蒜客 2019 icpc 上海网络赛 Counting Sequences II https://nanti.jisuanke.com/t/41413
题意是 有n个位置(1e18) 每个位置可以发1-m (2e5) 问每个偶数出现偶数次的排列有多少个
#include<bits/stdc++.h> #define ll long long using namespace std; const int maxn=2e5+10; const int mod=1e9+7; int A[maxn]; int B[maxn]; ll quick(ll x,ll n){ ll ans=1; x%=mod; while(n){ if(n&1) ans=ans*x%mod; x=x*x%mod; n=n/2; } return ans; } void init(){ int n=maxn-1; A[0]=1; for(int i=1;i<=n;i++) A[i]=1ll*A[i-1]*i%mod; B[n]=quick(A[n],mod-2); B[0]=1; for(int i=n-1;i>=1;i--) B[i]=1ll*B[i+1]*(i+1)%mod; } int C(int n,int x){ if(n<x) return 0; return 1ll*A[n]*B[x]%mod*B[n-x]%mod; } int main(){ init(); int T; scanf("%d",&T); while(T--){ ll n,m; scanf("%lld %lld",&n,&m); ll ans=0; int x=quick(2,m/2); int y=1ll*quick(x,mod-2); for(int i=0;i<=m/2;i++){ ans+=C(m/2,i)%mod*quick(m-2*i,n)%mod; } printf("%lld\n",1ll*ans%mod*y%mod); } }
hdu 1521 简单题
#include<bits/stdc++.h> using namespace std; const int maxn=20; int a[maxn]; int c[maxn]; int d[maxn]; int A[maxn]; int main(){ int n,m; while(scanf("%d %d",&n,&m)!=EOF){ memset(a,0,sizeof(a)); memset(d,0,sizeof(d)); memset(c,0,sizeof(c)); memset(A,0,sizeof(A)); for(int i=1;i<=n;i++) scanf("%d",&a[i]); A[0]=1; for(int i=1;i<=10;i++) A[i]=A[i-1]*i; c[0]=A[m]; for(int i=1;i<=n;i++){ for(int j=0;j<=10;j++){ d[j]=c[j]; c[j]=0; } for(int j=0;j<=10;j++){ for(int k=0;k<=a[i];k++){ if(j+k>m) continue; c[j+k]+=d[j]/A[k]; } } } printf("%d\n",c[m]); } }
hdu 1171 多重背包为什么要用母函数写....
#include<bits/stdc++.h> using namespace std; int a[60]; int b[60]; vector<int> vs; vector<int> vq; bool bbb[300000]; int main(){ int n; while(scanf("%d",&n) && n>=0){ vs.clear(); vq.clear(); int sum=0; for(int i=1;i<=n;i++){ scanf("%d %d",&a[i],&b[i]); sum+=a[i]*b[i]; } for(int i=1;i<=n;i++){ if(i==1){ for(int j=0;j<=b[i];j++){ vs.push_back(a[i]*j); } } else { for(int j=0;j<vs.size();j++){ for(int k=0;k<=b[i];k++){ if(bbb[a[i]*k+vs[j]]==1 || a[i]*k+vs[j]>sum/2) continue; vq.push_back(a[i]*k+vs[j]); bbb[a[i]*k+vs[j]]=1; } } vs.clear(); vs=vq; vq.clear(); for(int x=0;x<vs.size();x++){ bbb[vs[x]]=0; } } } int k=100000000; int A=0; int B=0; for(int i=0;i<vs.size();i++){ int x=min(vs[i],sum-vs[i]); int y=max(vs[i],sum-vs[i]); if(y-x<=k){ k=y-x; A=y; B=x; } } printf("%d %d\n",A,B); } }