HDU&POJトレーニングレコード4生成関数


1、Ignatius and the Princess III
トランスファゲート
題意を簡潔に述べる.
nをいくつかの整数に分割するシナリオ数を求める.
データ範囲
n≤120
問題解
まず,各数で構成できる数の生成関数を構築し,次に(1+x 1+x 2+x 3+...)を乗算する.(1+x2+x4+x6+...)(1+x3+x6+x9...)... そして多項式を展開し、xn項の係数が答えである.O(n 3)直接暴力で多項式を解くことができる.
コード#コード#
#include
#include
#include
#include
#include
using namespace std;
#define N 150

int n;
int a[N],b[N];

int main()
{
    while (~scanf("%d",&n))
    {
        for (int i=0;i<=n;++i) a[i]=1,b[i]=0;
        for (int i=2;i<=n;++i)
        {
            for (int j=0;j<=n;++j)
                for (int k=0;k<=n;k+=i)
                    b[j+k]+=a[j];
            for (int j=0;j<=n;++j) a[j]=b[j],b[j]=0;
        }
        printf("%d
"
,a[n]); } }

2、Square Coins
トランスファゲート
題意を簡潔に述べる.
nをいくつかの完全な平方数に分割するスキーム数を求める.
データ範囲
n≤300
問題解
前の問題と同様に,各完全二乗数の生成関数を構築し,乗算する(1+x 1+x 2+x 3+...)(1+x2+x4+x6+...)(1+x3+x6+x9...)... そして多項式を展開し、xn項の係数が答えである.O(n 3)直接暴力で多項式を解くことができる.
コード#コード#
#include
#include
#include
#include
#include
using namespace std;
#define N 600

int n,m,qr[N],a[N],b[N];

int main()
{
    for (int i=1;i<=18;++i) qr[i]=i*i;
    while (~scanf("%d",&m))
    {
        if (!m) break;
        for (n=1;qr[n+1]<=m;++n);
        for (int i=0;i<=m;++i) a[i]=1,b[i]=0;
        for (int i=2;i<=n;++i)
        {
            for (int j=0;j<=m;++j)
                for (int k=0;k<=m;k+=qr[i])
                    b[j+k]+=a[j];
            for (int j=0;j<=m;++j) a[j]=b[j],b[j]=0;
        }
        printf("%d
"
,a[m]); } }

3、Holding Bin-Laden Captive!
トランスファゲート
題意を簡潔に述べる.
a個1,b個2,c個5があり,最小の組み合わせられない数を尋ねる.
データ範囲
a,b,c≤1000
問題解
従来の方法とは差が少ない1,2,5の生成関数をそれぞれ構築し,ここでは有限数列(1+x 1+x 2+x 2+…+xa)(1+x 2+x 4+…+x 2 b)(1+x 5+x 10+…+x 5 c)が多項式を展開し,次いで回数が最も小さい係数のない項を探せば時間O(n 2)
コード#コード#
#include
#include
#include
#include
#include
using namespace std;
#define N 10000

int x,y,z,ans;
int a[N],b[N];

int main()
{
    while (~scanf("%d%d%d",&x,&y,&z))
    {
        if (!x&&!y&&!z) break;
        memset(a,0,sizeof(a));memset(b,0,sizeof(b));
        for (int i=0;i<=x;++i) a[i]=1;
        for (int i=0;i<=x;++i)
            for (int j=0;j<=y<<1;j+=2)
                b[i+j]+=a[i];
        for (int i=0;i<=x+(y<<1);++i) a[i]=b[i],b[i]=0;
        for (int i=0;i<=x+(y<<1);++i)
            for (int j=0;j<=z*5;j+=5)
                b[i+j]+=a[i];
        for (ans=1;ans<=x+(y<<1)+z*5;++ans)
            if (!b[ans]) break;
        printf("%d
"
,ans); } }

4、Big Event in HDU
トランスファゲート
題意を簡潔に述べる.
n種類のものがあり、それぞれの重み値はvで、m個あります.これらのものを2つに分けて、重みとできるだけ近づけるようにします.
データ範囲
0問題解
読み込み終了が負の数であると判断し、-1とは限らず、長い間waしていた.まず生成関数でそれらの重み値を組み合わせることができることを求めて、[m/2..0]の範囲内のものを列挙すればよい時間O(nmv)
コード#コード#
#include
#include
#include
#include
#include
using namespace std;
#define N 300000

int n,m,x,y;
int a[N],b[N];

int main()
{
    while (~scanf("%d",&n))
    {
        if (n<0) break;
        memset(a,0,sizeof(a));memset(b,0,sizeof(b));
        scanf("%d%d",&x,&y);
        for (int i=0;i<=y;++i) a[x*i]=1;
        m=x*y;
        for (int i=2;i<=n;++i)
        {
            scanf("%d%d",&x,&y);
            for (int j=0;j<=m;++j)
                for (int k=0;k<=y;++k)
                    b[j+x*k]+=a[j];
            m+=x*y;
            for (int j=0;j<=m;++j) a[j]=b[j],b[j]=0;
        }
        for (int i=m/2;i>=0;--i)
            if (a[i])
            {
                printf("%d %d
"
,m-i,i); break; } } }

5、Blocks
トランスファゲート
題意を簡潔に述べる.
赤と黄と青の緑でn個の格子に染色して、赤と緑が偶数でなければならないことを要求して、方案数を求めます.10007を型抜きします.
データ範囲
T≤100,1≤n≤109
問題解
配列問題にかかわるため,指数型生成関数を用いてシナリオ数をanとし,生成関数を書くことができる:(1+x 22!+x 44!+...)2(1+x+x22!+x33!+...)2=(ex+e−x2)2⋅e2x=e2x+e−2x+24⋅e2x=e4x+2e2x+14=(4n−1+2n−1)∑n>0xnn! だからこの問題の答えは4 n−1+2 n−1です
コード#コード#
#include
#include
#include
#include
#include
using namespace std;
#define Mod 10007

int T,n;

int fast_pow(int a,int p)
{
    int ans=1;
    for (;p;p>>=1,a=a*a%Mod)
        if (p&1)
            ans=ans*a%Mod;
    return ans;
}
int main()
{
    scanf("%d",&T);
    while (T--)
    {
        scanf("%d",&n);
        printf("%d
"
,(fast_pow(4,n-1)+fast_pow(2,n-1))%Mod); } }

6、配列の組み合わせ
トランスファゲート
題意を簡潔に述べる.
n種類の物品があって、それぞれai個があって、これらの物品の中でm個を選んで、配列数を求めます.
データ範囲
1≤n,m≤10
問題解
配列問題は,指数型生成関数指数で出現回数を表し,生成関数を書くには∏i=1 n(1+x+x 22!+x 33!+…+xaiai!)であるべきである.それから多項式を展開して第m項の係数を求める過程の中で暴力は多項式乗算をしてdoubleを使って、c++強転intが0に向かって整頓することに注意します
コード#コード#
#include
#include
#include
#include
#include
using namespace std;

int n,m,x;
double mul[50],a[50],b[50];

int main()
{
    mul[0]=1.0;
    for (int i=1;i<=10;++i) mul[i]=mul[i-1]*(double)i;
    while(~scanf("%d%d",&n,&m))
    {
        memset(a,0,sizeof(a));memset(b,0,sizeof(b));
        scanf("%d",&x);
        for (int i=0;i<=min(m,x);++i) a[i]=1.0/mul[i];
        for (int i=2;i<=n;++i)
        {
            scanf("%d",&x);
            for (int j=0;j<=m;++j)
                for (int k=0;k<=min(m,x);++k)
                    b[j+k]+=a[j]*(1/mul[k]);
            for (int j=0;j<=m;++j) a[j]=b[j],b[j]=0.0;
        }
        printf("%.0lf
"
,a[m]*mul[m]); } }