普通型母函数、指数型母函数
前言
emmm争取在12点发出去!
母函数(生成函数),简直是解决组合问题的利器!
本文写的思路很短,而且无注释,是因为母函数入门就套公式嘛
- 普通型母函数
http://www.wutianqi.com/?p=596
https://wenku.baidu.com/view/10c0ddfb4693daef5ef73d77.html?from=search - 指数型母函数
http://www.wutianqi.com/?p=2644
硬币 - SZUCPC 2017 Winter
思路
校赛题目,忽然发现可以用母函数做!(当然啦正解用的是DP)
范围较大,注意优化
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#include <map>
using namespace std;
const int N = 1e6 + 15;
int c1[N], c2[N];
void solve(){
int lim = 2;
memset(c1, 0, sizeof(c1));
memset(c2, 0, sizeof(c2));
c1[0] = c1[1] = c1[2] = 1;
for(int i = 2; i < N; i <<= 1){
for(int k = 0; k <= (i << 1); k += i){
for(int j = 0; j <= lim && j + k < N; j++){
c2[j + k] += c1[j];
}
lim += k;
}
for(int j = 0; j <= lim && j < N; j++){
c1[j] = c2[j];
c2[j] = 0;
}
}
}
int main(){
solve();
int t, csn = 1;
scanf("%d", &t);
while(t--){
int n;
scanf("%d", &n);
printf("Case #%d: %d\n", csn++, c1[n]);
}
}
Square Coins - HDU 1398
题意
求由数1、4、9、…、17^2组成数n的方案数(每种数可以用无数次)
思路
同样是构造母函数(似乎也可以用背包做)
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 300 + 15;
int c1[N], c2[N];
void solve(){
fill(c1, c1 + N, 1);
memset(c2, 0, sizeof(c2));
for(int i = 2; i <= 17; i++){
for(int j = 0; j < N; j++){
for(int k = 0; j + k < N; k += (i*i)){
c2[j + k] += c1[j];
}
}
for(int j = 0; j < N; j++){
c1[j] = c2[j];
c2[j] = 0;
}
}
}
int main(){
solve();
int n;
while(scanf("%d", &n) && n){
printf("%d\n", c1[n]);
}
}
Ignatius and the Princess III - HDU 1028
题意
问数n由数1、2、…、n组成的方案数(每种数都是无限的)
思路
已经懒得写思路了!
同上题
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 120 + 15;
int c1[N], c2[N];
void solve(){
fill(c1, c1 + N, 1);
memset(c2, 0, sizeof(c2));
for(int i = 2; i < N; i++){
for(int j = 0; j < N; j++){
for(int k = 0; j + k < N; k += i){
c2[j + k] += c1[j];
}
}
for(int j = 0; j < N; j++){
c1[j] = c2[j];
c2[j] = 0;
}
}
}
int main(){
solve();
int n;
while(~scanf("%d", &n)){
printf("%d\n", c1[n]);
}
}
Holding Bin-Laden Captive! - HDU 1085
题意
给定1元、2元、5元硬币的数量,问最小的不能由他们凑出来的钱是几?
思路
首先构造母函数
构造完了之后计算,最后枚举第一个为0的位置,该位置即是最小的凑不出来的钱
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 8000 + 15;
int c1[N], c2[N];
int solve(int b[4]){
int d[] = {0, 1, 2, 5};
memset(c2, 0, sizeof(c2));
memset(c1, 0, sizeof(c1));
fill(c1, c1 + b[1] + 1, 1);
for(int i = 2; i <= 3; i++){
for(int j = 0; j < N; j++){
for(int k = 0; k <= b[i]*d[i] && j + k < N; k += d[i]){
c2[j + k] += c1[j];
}
}
for(int j = 0; j < N; j++){
c1[j] = c2[j];
c2[j] = 0;
}
}
for(int i = 1; i < N; i++){
if(c1[i] == 0) return i;
}
return -1;
}
int main(){
int b[4];
while(~scanf("%d%d%d", &b[1], &b[2], &b[3]) && b[1] + b[2] + b[3]){
printf("%d\n", solve(b));
}
}
Big Event in HDU - HDU 1171
题意
给定n种物品的价值val[i]和数量cnt[i],给出将他们尽量均分的方案
思路
首先可能能用背包做,当然这里用母函数做
构造完母函数计算完后,从(总价值+1)/2开始枚举,这样能尽量均分(或完全均分),第一个非0的位置就是均分方案
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 3e5 + 15;
int c1[N], c2[N];
void solve(int v[], int cnt[], int n, int& ans1, int& ans2){
memset(c2, 0, sizeof(c2));
memset(c1, 0, sizeof(c1));
c1[0] = 1;
int lim = 0;
for(int i = 1; i <= n; i++){
for(int k = 0; k <= cnt[i] * v[i]; k += v[i]){
for(int j = 0; j <= lim && j + k < N; j++){
c2[j + k] += c1[j];
}
}
lim += cnt[i] * v[i];
for(int j = 0; j <= lim; j++){
c1[j] = c2[j];
c2[j] = 0;
}
}
int sum = 0;
for(int i = 1; i <= n; i++){
sum += v[i] * cnt[i];
}
for(int i = (sum + 1)/2; i < N; i++){
if(c1[i]){
ans1 = i;
break;
}
}
ans2 = sum - ans1;
}
int v[65], cnt[65];
int main(){
int n;
while(~scanf("%d", &n) && n >= 0){
for(int i = 1; i <= n; i++){
scanf("%d%d", &v[i], &cnt[i]);
}
int ans1, ans2;
solve(v, cnt, n, ans1, ans2);
printf("%d %d\n", ans1, ans2);
}
}
排列组合 - HDU 1521
思路
指数型母函数模板题
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
const int N = 20 + 15;
int fac[N];
int cnt[N];
double c1[N], c2[N];
void getFac(){
fac[0] = 1;
for(int i = 1; i < N; i++){
fac[i] = fac[i - 1] * i;
}
}
double solve(int n, int m){
fill(c1, c1 + N, 0);
fill(c2, c2 + N, 0);
c1[0] = 1;
for(int i = 1; i <= n; i++){
for(int j = 0; j < N; j++){
for(int k = 0; k <= cnt[i] && j + k < N; k++){
c2[j + k] += c1[j]/fac[k];
}
}
for(int j = 0; j < N; j++){
c1[j] = c2[j];
c2[j] = 0;
}
}
return c1[m] * fac[m];
}
int main(){
getFac();
int n, m;
while(~scanf("%d%d", &n, &m)){
for(int i = 1; i <= n; i++){
scanf("%d", &cnt[i]);
}
printf("%.0f\n", solve(n, m));
}
}