[SinGuLaRiTy] 組合數學題目複習

【SinGuLaRiTy-1021】 Copyright (c) SinGuLaRiTy 2017.  All Rights Reserved.html

[CQBZOJ 2011] 計算係數

題目描述

給定一個多項式(ax + by)^k,請求出多項式展開後x^n y^m項的係數。ios

輸入

共一行,包含 5 個整數,分別爲a,b,k,n,m,每兩個整數之間用一個空格隔開。express

輸出

輸出共 1 行,包含一個整數,表示所求的係數,這個係數可能很大,輸出對10007 取模後的結果。編程

樣例數據

樣例輸入 樣例輸出
1 1 3 1 2
3

 

 

 

解析

這道題能夠用遞推解決:
設f[i][j]爲(ax+by)^i的x^j*y^(n-j)的係數。顯然能夠獲得公式: f[i][j]=(f[i-1][j-1]*a+f[i-1][j]*b)007。
時間複雜度O(N^2)less

Code

#include<cstring>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstdlib>

#define MOD 10007

using namespace std;

int f[1005][1005];

int main()
{
    int a,b,k,n,m;
    scanf("%d%d%d%d%d",&a,&b,&k,&n,&m);
    a%=MOD;
    b%=MOD;
    f[2][1]=a;
    f[1][2]=b;
    for(int i=1;i<=k;i++)
    {
        for(int j=1;j<=k;j++)
        {
            if(!(i==1&&j==2||i==2&&j==1))
                f[i][j]=(f[i-1][j]*a+f[i][j-1]*b)%MOD;
        }
    }
    printf("%d",f[n+1][m+1]%MOD);
    return 0;
}

[CQBZOJ 2713] 計算組合數

題目描述

給定正整數n, k,計算C(n, k)。答案保證在2^31之內。ide

輸入

多組數據,每組數據僅一行,即2個整數n和k (n>=1) and k (0<=k<=n). 以2個0結束輸入。測試

輸出

對每一個數據,輸出對應的答案。ui

樣例數據

樣例輸入 樣例輸出
4 2
10 5
49 6
0 0
6
252
13983816

 

 

 

 

解析

板題,無須解釋。this

Code

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cmath>
#include<cstring>
#define LL long long
using namespace std;
LL C(LL n ,LL k)
{
    if(k>n/2)
        k=n-k;
    LL a=1;
    LL b=1;
    int i;
    for(i=1;i<=k;i++)
    {
        a*=n-i+1;
        b*=i;
        if(!(a%b))
        {
            a/=b;
            b=1;
        }
    }
    return a/b;
}
int main()
{
    LL n,k;
    for(;;)
    {
        scanf("%lld%lld",&n,&k);
        if(!n&&!k)
            break;
        printf("%lld\n",C(n,k));
    }
    return 0;
}

[CQBZOJ 1490] 組合數學一

題目描述

N個盒子排成一行(1<=N<=20)。你有A個紅球和B個藍球。0 <= A <= 15, 0 <= B <= 15。球除了顏色沒有任何區別。你能夠將球放進盒子。一個盒子能夠同時放進兩種球,也能夠只放一種,也能夠空着。球沒必要所有放入盒子中。編程計算有多少种放置球的方法。url

輸入

一行,N,A,B,用空格分開。

輸出

一行,輸出放置方案總數。

樣例數據

樣例輸入 樣例輸出
2 1 1
9

 

 

 

解析

其實紅球和藍球性質相同,分別放紅球放藍球,再運用乘法原理相乘便是方案數。而因爲不用放完,數據又是如此的小,咱們能夠用兩層for循環枚舉放球的數量,將全部結果累加便可。可是問題又來了,盒子是有序的,這意味着放進不一樣的盒子是不一樣的方案,那麼用xi表示第i個盒子裏放的球,考慮以下的一個方程:
    x1+x2+x3+……+xk=n(n爲球的數量,k爲盒子的數量)
    x1~xk的取值範圍是0~n的正整數,咱們能夠將全部x所有+1,那麼就變成以下的方程
    x1+x2+x3+……+xk=n+k
這樣就轉變爲了一個小球擋板問題(將n+k個小球用k-1個擋板隔開,分紅k份的方案數),也就是n+k-1個縫選擇k-1個安放擋板,方案數爲C(n+k-1,k-1)
那麼全部困難就迎刃而解了(注意int會炸,要用long long)

Code

#include<iostream>
#include<cstdio>
#include<cstring>
#define ll long long unsigned
using namespace std;
ll c(ll n,ll r)
{
    ll ans=1;
    ll i;
    for(i=1;i<=r;i++)
    {
        ans*=(n-i+1);
        ans/=i;
    }
    return ans;
}
int main()
{
    ll k,a,b;
    cin>>k>>a>>b;
    cout<<c(a+k,a)*c(b+k,b);
    return 0;
}

[CQBZOJ 1491] 組合數學二

題目描述

有n個正整數排成一行。你的目的是要從中取出一個或連續的若干個數,使它們的和可以被k整除。 例如,有6個正整數,它們依次爲一、二、六、三、七、4。若k=3,則你能夠取出一、二、6,或者二、六、三、7,也能夠僅僅取出一個6或者3使你所取的數之和能被3整除。固然,知足要求的取法不止以上這4種。事實上,一共有7種取法知足要求。 給定n和k,以及這n個數。你的任務就是肯定,從這n個數中取出其中一個數或者若干連續的數使它們的和能被k整除有多少方法。 因爲取法可能不少,所以你只須要輸出它mod 1234567的值便可。

輸入

第一行有兩個正整數,分別表明n和k。輸入數據保證有n小於等於500 000,k小於等於100 000。 如下n行每行一個正整數。這些正整數保證都不大於10 000。

輸出

一個正整數。它應該是你的答案mod 1234567的結果。

樣例數據

樣例輸入 樣例輸出
6 3
1
2
6
3
7
4
7

 

 

 

 

 

 

解析

SUM[i]是表明前i個數的和 
當(SUM[i]-SUM[j]) MOD k=0 這時[j+1,i]就是知足的一個區間 一個方案了 
而咱們求的是(SUM[i]-SUM[j]) MOD k=0 這樣的方案總個數 
咱們又能夠推出 上式等價於SUM[i] MOD k=SUM[j] MOD k 
因此咱們就是求SUM[i] MOD k=SUM[j] MOD k 的方案個數了 

假設 sum[i],sum[j],..sum[k](共bn個) 都是 MOD k 餘數爲k-1的sum 
那麼從上面bn個sum中任意選取兩個就能得出(SUM[i]-SUM[j]) MOD k=0 
那麼在bn個sum中怎麼配對呢 

(下面的sum[bn]表示上述bn個sum中的第n個sum)
很簡單 先是sum[b1]與sum[b2] sum[b3] ...sum[bn] (bn-1 個) 
       而後sum[b2]與sum[b3] sum[b4] ...sum[bn] (bn-2 個) 
       而後sum[b3]與sum[b4] sum[b5] ...sum[bn] (bn-3 個) 
       ............ 
       最後sum[bn-1]與sum[bn]         ( 1 個) 

方案總數=n-1+n-2+n-3+...+1=bn*(bn-1) div 2 
因此 當sum mod k的餘數爲k-1時有bn*(bn-1) div 2個方案總數了 
就這樣依次得出餘數爲k-1 k-2 k-3 ...0的時候方案總數 再相加一下得出答案 
因此在讀入一個數的時候就計算sum而後計算sum mod k 的餘數 
而b[j]表示餘數爲j的sum個數 此時根據上面新得出的更新相應的b[j] 
這樣在讀入完畢以後就能夠根據b[j]直接計算總方案數了

Code

#include<cstdio>
#include<cstring>
#define MAXN 500000
#define MOD 1234567
int mod_num[MAXN+40];
int main()
{
    int n,k,s=0,i,x;
    scanf("%d%d",&n,&k);
    mod_num[0]++;
    for(i=1;i<=n;i++)
    {
        scanf("%d",&x);
        s=(s+x)%k;
        mod_num[s]++;
    }
    s=0;
    for(i=0;i<k;i++)
        s=(s+mod_num[i]*(mod_num[i]-1LL)/2)%MOD;
    printf("%d",s);
    return 0;
}

[CQBZOJ 1492] 組合數學三

題目描述

八是個頗有趣的數字啊。八=發,八八=爸爸,88=拜拜。固然最有趣的仍是8用二進制表示是1000。怎麼樣,有趣吧。固然題目和這些都沒有關係。 某我的很無聊,他想找出[a,b]中能被8整除卻不能被其餘一些數整除的數。

輸入

第一行一個數n,表明不能被整除的數的個數。 第二行n個數,中間用空格隔開。 第三行兩個數a,b,中間一個空格。 a < =b < =1000000000

輸出

 一個整數,爲[a,b]間能被8整除卻不能被那n個數整除的數的個數。

樣例數據

樣例輸入 樣例輸出
3
7764 6082 462
2166 53442
6378

 

 

 

 

解析

用Ax來表示在[ a , b ]能被x整除的數,則問題變爲求 A8 ∩ (b - a + 1 - A(a[1]) ∪ A(a[2]) ∪ A(a[3]) ∪ .... ∪ A(a[n]) )
Ans = [a , b]中全部能被8整除的數的個數 - [a , b]中既能被8整除又能被其餘數整除的數的個數
容斥原理求[a , b]中既能被8整除又能被其餘數整除的數的個數

Code

#include<iostream>
#include<cstdio>
#include<cmath>
using namespace std;
typedef unsigned long long ULL;
int a[16],b[16];
int N;
ULL A,B,ans;
ULL gcd(ULL a,ULL b){return b?gcd(b,a%b):a;}
void Dfs(int i,ULL num,int cnt){
    if(i>N){
        if(cnt&1)
            ans+=B/num-A/num;
        else ans-=B/num-A/num;
        return ;
    }
    Dfs(i+1,num/gcd(num,a[i])*a[i],cnt+1);
    Dfs(i+1,num,cnt);
}
int main(){
    scanf("%d",&N);
    for(int i=1; i<=N; ++i)
        scanf("%d",&a[i]);
    scanf("%llu%llu",&A,&B);
    Dfs(1,8,1);
    printf("%llu\n",ans);
    return 0;
}

[POJ 1173] 條形碼

題目描述

條形碼以下圖所示,由黑白兩色構成,每種顏色分佈必定的寬度。寬度是一個有上限的整數值。例以下圖的條形碼有4個條形,寬度分別是1,2,3,4,總寬度爲:1+2+3+1 =7

定義BC(n,k,m) 表示有k個條形,每一個條形的寬度不超過m,總寬度爲n的條形碼的總個數。例如BC(7,4,3)表示了以下全部的條形碼:

0: 1000100  |  8: 1100100
1: 1000110  |  9: 1100110
2: 1001000  | 10: 1101000
3: 1001100  | 11: 1101100
4: 1001110  | 12: 1101110
5: 1011000  | 13: 1110010
6: 1011100  | 14: 1110100
7: 1100010  | 15: 1110110

其中1表示黑色,0表示白色。冒號前的數字表示了它的字典序號。上圖中的條形碼的序號爲4。

你的任務是讀入n, k, m,計算知足要求的條形碼的總個數,以及讀入若干個條形碼,計算對應的字典序號。

輸入

第1行:3個整數n,k,m(1 <= n,k,m <= 33)
第2行:1個整數s(0 <= s <= 100).
接下來s行,每行1個01串,表示一個條形碼

輸出

第1行:1個整數,表示知足要求的條形碼的總個數
接下來s行,每行1個整數,表示對應條形碼的字典序

樣例數據

樣例輸入 樣例輸出
7 4 3
5
1001110
1110110
1001100
1001110
1000100
16
4
15
3
4
0

 

 

 

 

 

 

解析

題目能夠轉化爲:有n的長度,k個木板,每一個木板的長度最多爲m,問有多少種組合方式,對於給定的組合是字典序的第多少個。

因而有了思路:用dp[i][j]表示後i個木板組成長度爲j的有多少種狀況。肯定給定的組合是第幾個的話,就從前日後數,奇數的木板,假如給定的長度爲3,那麼就答案就加上當它爲1和2的時候後面的狀況數,偶數的是長度從大到小加上它後面的狀況。

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
typedef long long ll;
ll dp[40][40];
int num[40];
char s[110];
int n,k,m;
int main()
{
    int i,j,a,b,p,cas,pos,len;
    ll ans,ret;
    while(~scanf("%d%d%d",&n,&k,&m))
    {
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;
        for(i=1;i<=k;i++)
           for(j=1;j<=n;j++)
           {
               for(a=1;a<=m && j-a>=0;a++)
                  dp[i][j]+=dp[i-1][j-a];
           }
        ans=0;
        printf("%I64d\n",dp[k][n]);
        scanf("%d",&cas);
        while(cas--)
        {
            scanf("%s",s+1);
            pos=0;
            for(i=1;i<=k;i++)
            {
                pos++;
                num[i]=1;
                while(pos<n && s[pos]==s[pos+1])
                {
                    num[i]++;
                    pos++;
                }
            }
            len=n;
            ans=0;
            for(i=k;i>=1;i--)
            {
                p=k-i+1;
                if(p&1)
                  for(j=1;j<num[p] && len-j>0;j++)
                     ans+=dp[i-1][len-j];
                else
                  for(j=min(m,len);j>num[p];j--)
                     ans+=dp[i-1][len-j];
                len-=num[p];
            }
            printf("%I64d\n",ans);
        }
    }
    return 0;
}

[HDU 4390] 表達式計數 

題目描述

給出n個數,b1,b2,b3……bn,構造n個數,a1,a2,……an(ai>1),使得a1*a2*a3……an=b1*b2……bn;
問一共有多少種數列a1,a2,……an知足上述條件。

輸入

包含多組輸入數據
每組數據第一行有1個整數n(1<=n<=20)
每組數據第 二行有n個整數第i個數表示bi.(1<bi<=1000000)且b1*b2*…*bn <10^25)。

輸出

對於每組測試數據,輸出有多少種數列知足狀況,結果對1e9+7取餘

樣例數據

樣例輸入 樣例輸出
2
3 4
4

 

 

 

 

解析

首先是將全部 的b進行素因子分解,則a和b的因子是徹底一致的。
剩下的即是將全部b的因子,分給a
咱們考慮某個素因子pi,若是有ci個,便成了子問題將ci個相同的物品放入到n個不一樣的容器中,種數爲多少
可是要求ai>1,也就是容器不能爲空,這是個問題。
咱們考慮的是什麼的狀況,而後減去假設有一個肯定是空的狀況,發現能夠用容斥原理解決
咱們假設f[i]表示有i個容器的結果,c(n,i)*f[i]
將m個物品放到到不一樣的n個容器中,結果爲c(n+m-1,n-1)

Code

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long LL;
const int maxn=1005;
const int MOD=1000000007;
int p[maxn];
int a[maxn];
LL c[maxn][maxn];
void init()
{
    int i,j;
    for(i=0;i<maxn;i++)
    {
        c[i][i]=c[i][0]=1;
        for(j=1;j<i;j++)
            c[i][j]=(c[i-1][j]+c[i-1][j-1])%MOD;
    }
}
int getnum(int m,int n){
    return c[m+n-1][n-1];
}
LL solve(int cnt,int n){
    int i,j;
    LL ans=1;
    for(i=0;i<=cnt;i++)
        ans=(ans*getnum(a[i],n))%MOD;
    for(i=1;i<n;i++)
    {
        LL tmp=c[n][i];
        for(j=0;j<=cnt;j++)
            tmp=(tmp*getnum(a[j],n-i))%MOD;
        if(i&1)
            ans=((ans-tmp)%MOD+MOD)%MOD;
        else
            ans=(ans+tmp)%MOD;
    }
    return ans;
}
int main()
{
    init();
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        int t,i,j;
        int cnt=0;
        for(i=0;i<n;i++)
        {
            scanf("%d",&t);
            for(j=2;j*j<=t;j++)
            {
                while(t%j==0){
                    p[cnt++]=j;
                    t/=j;
                }
            }
            if(t>1)
                p[cnt++]=t;
        }
        sort(p,p+cnt);
        a[0]=1;
        i=0;
        for(j=1;j<cnt;j++)
        {
            if(p[j]==p[j-1])
                a[i]++;
            else
                a[++i]=1;
        }
        cnt=i;
        printf("%lld\n",solve(cnt,n));
    }
    return 0;
}

[CQBZOJ 1986 & COCI11-12 #4 & BZOJ 3181] 糾結的數(BROJ)

題目描述

找出第N小的正整數X,知足條件X的最小的素因子是P。若是X的值超過10^9,則輸出0。

輸入

第1行:2個整數N和P (1 ≤ N, P ≤ 10^9),P必定是素數

輸出

1行:知足條件的數X或者0

樣例數據

樣例輸入 樣例輸出
2 3
9

 

 

 

解析

實在是作不出來了。首先到官網上看了看題解:

To solve this for large values of P we will use modification of the sieve of Eratosthenes. Size of our sieve will be 109 / P. Integers in the sieve represent multiples of P. During the execution of this algorithm we can find smallest prime factors or mark only multiples of prime numbers smaller than P as in the official solution.
For smaller values of P we can binary search through [1, 109 / P], again looking at these numbers as the corresponding multiples of P. For some number we must find the number of integers not greater and relatively prime with that number. We can do this by using inclusionexclusion principle with prime numbers less than P.
With careful implementation this solution can work for much larger values of P than requested for this subtask.
We can also solve this task for smaller value of P by making use of periodic behaviour of smallest prime factors. Let A(n) be the smallest prime factor of n, B(k) the k-th prime number, and T(k) the product of first k primes. For A(n) ≤ B(k), A(n + T(k)) = A(n) holds. So it’s enough to know A(n) for n ≤ T(k) in order to find the N-th prime who’s smallest prime factor is B(k)

......看完想罵人,仍是看中文的吧:

由於p是x的最小素因子,因此當p>sqrt(n),即p>sqrt(10^9)≈10^5時直接輸0。
對於剩下的部分,先篩出小於p的素數。
當p>=29時,直接標記Max(10^9)/p之內小於p素數的倍數.
當p<29時,二分答案+容斥原理(能被p整除,不能被小於p的其它整素數整除)

(Tips: 不知道如何肯定P的最佳值。在CQBZOJ上測試出: p若取到24如下,會RE;若取到52以上,會WA(有人說p=65時最優,(⊙_⊙?)). 具體狀況視不一樣評測環境而定)

Code

#include<cstdio>
#include<iostream>
#include<cstdlib>
#include<cmath>
#include<algorithm>

using namespace std;

typedef long long LL;

const int Max=1000000000;
const int N=20000010;
const int M=100010;

int ans,n,p,cnt,LCM,num,prime[M];
bool vis[M],used[N];
int gcd(int a,int b)
{
    int c;
    while(b)
    {
        c=b;
        b=a%b;
        a=c;
    }
    return a;
}

int lcm( int a, int b )
{
    return a/gcd(a,b)*b;
}

void dfs(int pos,int flag,int a,int b )
{
    if(pos>cnt)
    {
        if(flag&1)
            ans-=b/LCM-(a-1)/LCM;
        else
            ans+=b/LCM-(a-1)/LCM;
        return ;
    }
    int k=LCM;
    dfs(pos+1,flag,a,b);
    LCM=lcm(LCM,prime[pos]);
    dfs(pos+1,flag+1,a,b);
    LCM=k;
}

int devide(int l,int r)
{
    if(l==r)
        return l;
    int mid=(l+r)>>1;
    LCM=p;
    ans=0;
    dfs(1,0,p+1,mid);
    if(ans>=n)
        return devide(l,mid);
    else
        return devide(mid+1,r);
}

int main()
{
    scanf("%d%d",&n,&p);
    if(n==1)
    {
        printf("%d\n",p);
        return 0;
    }
    if(p*1LL*p*1LL>Max)
    {
        putchar(48);
        return 0;
    }
    for(int i=2;i<p;++i)
    {
        if(vis[i])
            continue;
        prime[++cnt]=i;
        for(int j=i;j<p;j+=i)
            vis[j]=1;
    }
    if(p<29)
    {
        --n;
        int v=devide(p+1,Max);
        for(int i=1;i<=cnt;++i)
            if(v%prime[i]==0)
            {
                v=0;
                break ;
            }
        printf("%d\n",v);
        return 0;
    }
    int mm=Max/p;
    for(int i=1;i<=cnt;++i)
        for(int j=prime[i];j<=mm;j+=prime[i])
            used[j]=1;
    int i;
    for(i=1;i<=mm;++i)
    {
        if(!used[i])
            num++;
        if(num==n)
            break ;
    }
    if(num==n)
        printf("%d\n",p*i);
    else
        putchar(48);
    return 0;
}

[CQBZOJ 2716] 無關的元素

題目描述

對於給定的n個數a1,a2,...,an,依次求出相鄰兩數之和,將獲得一個新數列。重複上述操做,最後結果將變成一個數。問這個數除以m的餘數與哪些數無關?
例如n=3,m=2時,第一次求和獲得a1+a2,a2+a3,再次求和獲得a1+2a2+a3,它除以2的餘數和a2無關。

輸入

第1行:2個整數n和m(1<=n<=10^5, 2 <=m<=10^9)

輸出

按升序列出與m無關的元素的序號,每行1個。
若與所有元素無關,輸出0

樣例數據

樣例輸入 樣例輸出
5 3
3

 

 

 

解析

只要判斷C(n-1,i-1)可否被 m整除便可。
作法是先分解m的質因數,而後計算1!~(n-1)! 包含m的質因數的個數
C(n-1,i-1) = (n-1)!/((i-1)!*(n-i)!)
只要判斷 剩下的質因數的個數是否大於等於m的任一個質因數的個數便可

Code

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define MAXN 100000
using namespace std;
int s[MAXN + 10][2];
int n,m;
int cnt;
int ans[20];
void dive(int x){
    int rt = sqrt(x + 0.5);
    for(int i = 2;i <= rt;i++){
        if(x % i == 0)
            s[++cnt][0] = i;
        while(x % i == 0){
            s[cnt][1]++;
            x /= i;
        }
    }
    if(x > 1){
        s[++cnt][0] = x;
        s[cnt][1] = 1;
    }
}
void dive(int x,int k){
    for(int i = 1;i <= cnt;i++){
        while(x % s[i][0] == 0){
            ans[i] += k;
            x /= s[i][0];
        }
        if(x == 1)
            break;
    }
}
bool check(){
    for(int i = 1;i <= cnt;i++)
        if(ans[i] < s[i][1])
            return false;
    return true;
}
int main(){
    scanf("%d%d",&n,&m);
    n--;
    dive(m);
    bool flag = true;
    if(m == 1) {
        flag = false;
        printf("1\n");
    }
    int ans = 1;
    for(int i = 1;i < n;i++){
        dive(n - i + 1,1);
        ans *= (n - i + 1);
        dive(i,-1);
        ans /= i;
        if(check()){
            printf("%d\n",i + 1);
            flag = false;
        }
    }
    if(flag)
        printf("0");
    return 0;
}

[HDU 4248] A Famous Stone Collector

題目描述

Mr. B loves to play with colorful stones. There are n colors of stones in his collection. Two stones with the same color are indistinguishable. Mr. B would like to
select some stones and arrange them in line to form a beautiful pattern. After several arrangements he finds it very hard for him to enumerate all the patterns. So he asks you to write a program to count the number of different possible patterns. Two patterns are considered different, if and only if they have different number of stones or have different colors on at least one position.

有N種石頭,每種石頭有A1,A2....AN個,現取出一些石頭組成序列,求能夠組成多少種序列。

輸入

Each test case starts with a line containing an integer n indicating the kinds of stones Mr. B have. Following this is a line containing n integers – the number of
available stones of each color respectively. All the input numbers will be nonnegative and no more than 100.

輸出

For each test case, display a single line containing the case number and the number of different patterns Mr. B can make with these stones, modulo 1,000,000,007, 
which is a prime number.

樣例數據

樣例輸入 樣例輸出

3

1 1 1

2

1 2

Case 1: 15

Case 2: 8

 

 

 

 

 

 

解析

咱們採用動態規劃的思想,劃分階段:按照石頭種類劃分階段。因而乎,對於第i種石頭,至關於以前石頭的顏色並不重要,藉助高中數學插板法的思想,假如以前的 i-1 種石頭,拼出了長度爲len,那麼,至關於有len+1個空,我們要放第 i 種石頭進去,因而乎,轉化成了經典問題。

因而設計狀態:DP[i][j] 表示 用前 i 種石頭,排出長度爲 j 的可能數這裏,第 i 種石頭互相沒有區別,len+1 個空有序,至關於有區別,能夠有空盒,因而,若是從第 i 種中放put個進去,狀況數應該是 C(put+len,put)

而後,狀態轉移的時候,枚舉在階段 i 放入 put 個,DP[i+1][j+put]+=DP[i][j] * C(put + j, put)  便可

複雜度 10000 ^ 2,能過……

Code

#include<cstdio>
#include<queue>
#include<cstring>
#include<iostream>
#include<algorithm>

#define LL long long
#define INF 0x3f3f3f3f
#define N 10010
#define mod 1000000007

using namespace std;

int num;
LL dp[110][N];
LL C[N][110];

void init()
{
    C[0][0]=1;
    for(int i=1;i<N;i++)
        for(int j=0;j<=100;j++)
        {
            if(!j)
                C[i][j]=C[i-1][j];
            else
                C[i][j]=(C[i-1][j]+C[i-1][j-1])%mod;
        }
}

int main()
{
    init();
    int cas=1;
    int n;
    while(~scanf("%d",&n))
    {
        memset(dp,0,sizeof(dp));
        dp[0][0]=1;

        int sum=0;
        for(int i=1;i<=n;i++)
        {
            scanf("%d",&num);
            sum+=num;
            for(int k=0;k<=num;k++)
                for(int j=k;j<=sum;j++)
                    dp[i][j]=(dp[i][j]+(dp[i-1][j-k]*C[j][k]%mod))%mod;
        }
        LL ans=0;
        for(int i=1;i<=sum;i++)
            ans=(ans+dp[n][i])%mod;
        printf("Case %d: ",cas++);
        printf("%lld\n",ans);
    }
    return 0;
}

[HDU 4372] Count The Buildings

題目描述

There are N buildings standing in a straight line in the City, numbered from 1 to N. The heights of all the buildings are distinct and between 1 and N. You can see F buildings when you standing in front of the first building and looking forward, and B buildings when you are behind the last building and looking backward. A building can be seen if the building is higher than any building between you and it.
Now, given N, F, B, your task is to figure out how many ways all the buildings can be.

不一樣高度的建築物排成一列。總共有N棟建築。從前看,能夠看到F棟建築;從後看,能夠看到B棟建築。問:這些建築有多少種知足條件的排列方式。

輸入

First line of the input is a single integer T (T<=100000), indicating there are T test cases followed.
Next T lines, each line consists of three integer N, F, B, (0<N, F, B<=2000) described above.

輸出

For each case, you should output the number of ways mod 1000000007(1e9+7).

樣例數據

樣例輸入 樣例輸出

2

3 2 2

3 2 1

2

1

 

 

 

 

 

解析

 

首先咱們知道一個結論:n的環排列的個數與n-1個元素的排列的個數相等,由於P(n,n)/n=(n-1)!。
能夠確定,不管從最左邊仍是從最右邊看,最高的那個樓必定是能夠看到的.
假設最高的樓的位置固定,最高樓的編號爲n,那麼咱們爲了知足條件,能夠在樓n的左邊分x-1組,右邊分y-1組,且用每組最高的那個元素表明這一組,那麼樓n的左邊,從左到右,組與組之間最高的元素必定是單調遞增的,且每組中的最高元素必定排在該組的最左邊,每組中的其它元素能夠任意排列(至關於這個組中全部元素的環排列)。右邊反之亦然。
而後,能夠這樣考慮這個問題,最高的那個樓左邊必定有x-1個組,右邊必定有y-1個組,且每組是一個環排列,這就引出了第一類Stirling數(n我的分紅k組,每組內再按特定順序圍圈的分組方法的數目)。
咱們能夠先把n-1個元素分紅x-1+y-1組,而後每組內部作環排列。再在全部組中選取x-1組放到樓n的左邊。因此答案是 ans(n,f,b)=C[f+b-2][f-1]*S[n-1][f+b-2] 。

<忘記第一類Stirling數?>

第一類Stirling數s(p,k)表示將p個元素分解爲k個非空循環排列的方案數。
◎它的遞推公式: s(p,k)=(p-1)*s(p-1,k)+s(p-1)(k-1), (1≤k≤p-1)
◎推導:若p個元素構成k個圓排列,考慮第p個元素的狀況:
  1> 若是前p-1個元素構成了k-1個圓排列,則第p個元素單獨構成一個環,方案數:
    s(p-1)(k-1)
  2> 若是前p-1個元素構成了k個圓排列,那麼第p個元素能夠插入到任意元素的左邊,方案數:
    (p-1)*s(p-1,k)
◎邊界條件:s(p,0)=0,p≥1;s(p,p)=1,p≥0.

[更多第一類Stirling數的推導及性質]

Code

#include<cstring>
#include<cstdio>
#include<cmath>

using namespace std;
typedef long long LL;

const int N=2005;
const LL MOD=1000000007;

LL C[N][N];
LL S[N][N];

void Init()
{
    for(int i=0;i<N;i++)
    {
        C[i][0]=1;
        C[i][i]=1;
        S[i][0]=0;
        S[i][i]=1;
        for(int j=1;j<i;j++)
        {
            C[i][j]=(C[i-1][j]%MOD+C[i-1][j-1]%MOD)%MOD;
            S[i][j]=((i-1)%MOD*S[i-1][j]%MOD+S[i-1][j-1]%MOD);
        }
    }
}

int main()
{
    LL t,n,f,b,ans;
    Init();
    scanf("%I64d",&t);
    while(t--)
    {
        scanf("%I64d%I64d%I64d",&n,&f,&b);
        if(f+b-2>n)//有一組數據須要特判,不然RE
            puts("0");
        else
        {
            ans=C[f+b-2][f-1]%MOD*S[n-1][f+b-2]%MOD;
            printf("%I64d\n",ans);
        }
    }
    return 0;
}    

[HDU 3208] Integer's Power

題目描述

LMY and YY are number theory lovers. They like to find and solve some interesting number theory problems together. One day, they become interested in some special numbers, which can be expressed as powers of smaller numbers.
For example, 9=3^2, 64=2^6, 1000=10^3 …
For a given positive integer y, if we can find a largest integer k and a smallest positive integer x, such that x^k=y, then the power of y is regarded as k.
It is very easy to find the power of an integer. For example:
The power of 9 is 2.
The power of 64 is 6.
The power of 1000 is 3.
The power of 99 is 1.
The power of 1 does not exist.
But YY wants to calculate the sum of the power of the integers from a to b. It seems not easy. Can you help him?

定義數的Power:對於給定的正整數y,若是咱們能夠找到最大的整數k和最小的正整數x,使得x ^ k = y,則y的Power被認爲是k。(1的Power不存在)
如今,要求求出從a到b的每個數的Power的和。

輸入

The input consists of multiple test cases.
For each test case, there is one line containing two integers a and b. (2<=a<=b<=10^18)
End of input is indicated by a line containing two zeros.

輸出

For each test case, output the sum of the power of the integers from a to b.

樣例數據

樣例輸入 樣例輸出

2 10

248832 248832

0 0

13

5

 

 

 

 

 

解析

對於一個數n,從1~n中假設有x個數是知足p^k形式的,這裏的k最多到62,那麼對於每個k,咱們須要找到這個數x知足x^k最接近n,那麼如今的問題就是對於每個k找出對應的x,那麼這個怎麼找呢?
咱們能夠這樣考慮,因爲是找最接近的,咱們能夠先大體肯定一個數r,那麼能夠經過r=pow(n,1/k)來計算,而後咱們分別計算(r-1)^k,r^k,(r+1)^k,而後看這三個數哪一個最接近n就好了,這裏要注意(r+1)^k計算時可能會超過LL,因此有一些處理。而後就是至關於容斥的部分了。

Code

#include<iostream>
#include<cstring>
#include<cstdio>
#include<cmath>

using namespace std;

typedef long long LL;

const LL INF=1e18+300;
const LL T=(LL)1<<31;

LL num[105];

LL multi(LL a,LL b)
{
    LL ans=1;
    while(b)
    {
        if(b&1)
        {
            double judge=1.0*INF/ans;
            if(a>judge) return -1;
            ans*=a;
        }
        b>>=1;
        if(a>T&&b>0) return -1;
        a=a*a;
    }
    return ans;
}

LL find(LL x,LL k)
{
    LL r=(LL)pow(x,1.0/k);
    LL t,p;
    p=multi(r,k);
    if(p==x) return r;
    if(p>x||p==-1) r--;
    else
    {
        t=multi(r+1,k);
        if(t!=-1&&t<=x) r++;
    }
    return r;
}

LL Solve(LL n)
{
    int i,k=0;
    memset(num,0,sizeof(num));
    if(n<=3) return n;
    num[1]=n;
    for(i=2;i<63;i++)
    {
        num[i]=find(n,i)-1;
        if(!num[i]) break;
    }
    k=i;
    for(int i=k-1;i>0;i--)
        for(int j=1;j<i;j++)
            if(i%j==0) num[j]-=num[i];
    LL ans=num[1];
    for(int i=2;i<k;i++)
        ans+=(i*num[i]);
    return ans;
}

int main()
{
    LL n,m;
    while(cin>>m>>n)
    {
        if(m==0&&n==0) break;
        cout<<Solve(n)-Solve(m-1)<<endl;
    }
    return 0;
}

[HDU 3944] DP?

題目描述

Figure 1 shows the Yang Hui Triangle. We number the row from top to bottom 0,1,2,…and the column from left to right 0,1,2,….If using C(n,k) represents the number of row n, column k. The Yang Hui Triangle has a regular pattern as follows.
C(n,0)=C(n,n)=1 (n ≥ 0) 
C(n,k)=C(n-1,k-1)+C(n-1,k) (0<k<n)
Write a program that calculates the minimum sum of numbers passed on a route that starts at the top and ends at row n, column k. Each step can go either straight down or diagonally down to the right like figure 2.
As the answer may be very large, you only need to output the answer mod p which is a prime.

在如圖一分佈的楊輝三角中,設計一條從最頂端到第n行第k列的路線,使其知足如下要求:1>每一步都向下或向右傾斜;2>通過的數字和最小。求出最小和模一個素數P的結果。

輸入

Input to the problem will consists of series of up to 100000 data sets. For each data there is a line contains three integers n, k(0<=k<=n<10^9) p(p<10^4 and p is a prime) . Input is terminated by end-of-file.

輸出

For every test case, you should output "Case #C: " first, where C indicates the case number and starts at 1.Then output the minimum sum mod p.

樣例數據

樣例輸入 樣例輸出

1 1 2

4 2 7

Case #1: 0
Case #2: 5

 

 

 

 

解析

<Lucas定理+預處理>

顯然第 n 行第 k 列的數就是組合數 C(n,k) ,答案知足對稱性。只須要討論 k <=n / 2 的狀況。
考慮從目的地往上走到頂點,由於組合數在 k <= n / 2 是遞增的,因此每次只要斜向上走,到了最左端,再往上走就能夠獲得最小的和,因此最小的和爲
C(n,k)+C(n-1,k-1)+C(n-2,k-2)+......+C(n-k,0)+n-k;

下面就是求這個數的問題。
用 C(n-k+1,0)替換掉 C(n-k,0)後,獲得:
C(n-k+1,0)+C(n-k+1,1)+C(n-k+2,2)+......+C(n-1,k-1)+C(n,k)+n-k
對於組合數有 C(n,k)=C(n-1,k-1)+C(n-1,k)
因此最左邊兩個數相加得 C(n-k+2,1),繼續與 C(n-k+2,2)相加獲得 C(n-k+3,2),一直加下去最後獲得 C(n+1,k)。
因此最小的和爲 C(n+1,k)+n-k。

Code

#include<iostream>
#include<cstdio>
#include<cstring>

#define N 10005

using namespace std;

bool prime[N];
int p[N],f[N][N],inv[N][N],cnt,pth[N];

void isprime()
{
    cnt=0;
    memset(prime,1,sizeof(prime));
    for(int i=2;i<N;i++)
    {
        if(prime[i])
        {
            p[++cnt]=i;
            pth[i]=cnt;
            for(int j=i+i;j<N;j+=i)
                prime[j]=0;
        }
    }
}

int ksm(int a,int b,int m)
{
    int ans=1;
    a%=m;
    while(b)
    {
        if(b&1)
        {
            ans=ans*a%m;
            b--;
        }
        b>>=1;
        a=a*a%m;
    }
    return ans;
}

void init()
{
    for(int i=1;i<=cnt;i++)
    {
        f[i][0]=inv[i][0]=1;
        for(int j=1;j<p[i];j++)
        {
            f[i][j]=(f[i][j-1]*j)%p[i];
            inv[i][j]=ksm(f[i][j],p[i]-2,p[i]);
        }
    }
}

int com(int n,int m,int P)
{
    if(m>n)
        return 0;
    if(m==n) 
        return 1;
    int t=pth[P];
    return f[t][n]*(inv[t][n-m]*inv[t][m]%P)%P;
}

int lucas(int n,int m,int P)
{
    if(m==0) 
        return 1;
    return com(n%P,m%P,P)*lucas(n/P,m/P,P)%P;
}

int main()
{
    int cas=1,n,m,P;
    isprime();
    init();
    while(scanf("%d%d%d",&n,&m,&P)!=EOF)
    {
        if(m<=n/2) 
            m=n-m;
        n++;
        printf("Case #%d: %d\n",cas++,(m%P+lucas(n,m+1,P))%P);
    }
    return 0;
}

[HDU 1695] GCD

題目描述

Given 5 integers: a, b, c, d, k, you're to find x in a...b, y in c...d that GCD(x, y) = k. GCD(x, y) means the greatest common divisor of x and y. Since the number of choices may be very large, you're only required to output the total number of different number pairs.
Please notice that, (x=5, y=7) and (x=7, y=5) are considered to be the same.
<You can assume that a = c = 1 in all test cases.>

給定a,b,c,d,k,要求在區間[a,b]內找到x,在[c,d]內找到y,使得gcd(x,y)=k。輸出全部知足條件的條件數。

輸入

The input consists of several test cases. The first line of the input is the number of the cases. There are no more than 3,000 cases.
Each case contains five integers: a, b, c, d, k, 0 < a <= b <= 100,000, 0 < c <= d <= 100,000, 0 <= k <= 100,000, as described above.

輸出

For each test case, print the number of choices. Use the format in the example.

樣例數據

樣例輸入 樣例輸出

2

1 3 1 5 1 1

11014 1 14409 9

Case 1: 9

Case 2: 736427

 

 

 

 

 

解析

只須要枚舉x,而後肯定另外一個區間裏面有多少個y就能夠了。所以問題轉化成爲區間(1, d)裏面與x互素的數的個數
先求出x的全部質因數,所以(1,d)區間裏面是x的質因數倍數的數都不會與x互素,所以,只須要求出這些數的個數,減掉就能夠了。
若是w是x的素因子,則(1,d)中是w倍數的數共有d/w個。

Code

#include<cstdio>
#include<cstring>
#include<vector>
#include<algorithm>

#define N 100005
#define ll long long

using namespace std;

vector<int> x[N];
bool is[N];

void prime()
{
    memset(is,false,sizeof(is));
    for(int i=0;i<N;i++)
        x[i].clear();
    for(int j=2;j<N;j+=2)
        x[j].push_back(2);
    for(int i=3;i<N;i+=2)
        if(!is[i])
            for(int j=i;j<N;j+=i)
            {
                is[j]=true;
                x[j].push_back(i);
            }
}
int work(int u,int s,int w)
{
    int cnt=0,v=1;
    for(int i=0;i<x[w].size();i++)
    {
        if((1<<i)&s)
        {
            cnt++;
            v*=x[w][i];
        }
    }
    int all=u/v;
    if(cnt%2==0)
        return -all;
    else
        return all;
}

int main()
{
    prime();
    int T,a,b,c,d,k;
    scanf("%d",&T);
    for(int cas=1;cas<=T;cas++)
    {
        scanf("%d%d%d%d%d",&a,&b,&c,&d,&k);
        if(k==0)
        {
            printf("Case %d: 0\n",cas);
            continue;
        }
        b/=k;
        d/=k;
        if (b>d)
        {
            a=b;
            b=d;
            d=a;
        }
        long long ans=0;
        for(int i=1;i<=d;i++)
        {
            k=min(i,b);
            ans+=k;
            for(int j=1;j<(1<<x[i].size());j++)
                ans-=work(k,j,i);
        }
        printf("Case %d: %I64d\n",cas,ans);
    }
    return 0;
}

[HDU 1124] Factorial

題目描述

The most important part of a GSM network is so called Base Transceiver Station (BTS). These transceivers form the areas called cells (this term gave the name to the cellular phone) and every phone connects to the BTS with the strongest signal (in a little simplified view). Of course, BTSes need some attention and technicians need to check their function periodically.

ACM technicians faced a very interesting problem recently. Given a set of BTSes to visit, they needed to find the shortest path to visit all of the given points and return back to the central company building. Programmers have spent several months studying this problem but with no results. They were unable to find the solution fast enough. After a long time, one of the programmers found this problem in a conference article. Unfortunately, he found that the problem is so called "Travelling Salesman Problem" and it is very hard to solve. If we have N BTSes to be visited, we can visit them in any order, giving us N! possibilities to examine. The function expressing that number is called factorial and can be computed as a product 1.2.3.4....N. The number is very high even for a relatively small N.

The programmers understood they had no chance to solve the problem. But because they have already received the research grant from the government, they needed to continue with their studies and produce at least some results. So they started to study behaviour of the factorial function.

For example, they defined the function Z. For any positive integer N, Z(N) is the number of zeros at the end of the decimal form of number N!. They noticed that this function never decreases. If we have two numbers N1<N2, then Z(N1) <= Z(N2). It is because we can never "lose" any trailing zero by multiplying by any positive number. We can only get new and new zeros. The function Z is very interesting, so we need a computer program that can determine its value efficiently.

求N!末尾「0」的個數。

輸入

There is a single positive integer T on the first line of input. It stands for the number of numbers to follow. Then there is T lines, each containing exactly one positive integer number N, 1 <= N <= 1000000000. 

輸出

For every number N, output a single line containing the single non-negative integer Z(N).

樣例數據

樣例輸入 樣例輸出

6

3

60

100

1024

23456

8735373

0

14

24

253

5861

2183837

 

 

 

 

 

 

 

 

 

解析

首先咱們要知道,已經在末尾產生的"0"是不會在後續的運算中消失的。
因而咱們能夠分析:在哪些狀況下能在末尾產生"0"? 咱們能夠發現,這就須要因數能夠分解獲得2和5。
顯然在N!中2的個數大於5的個數,因此只需求出5的個數便可
求 N! (1*2*3*4*5*...*N)裏有多少個5其實能夠轉化成:
N!中:是5的倍數的數+是5^2的倍數的數+5^3.....

Code

#include<cstdio>
#include<cmath>
#include<algorithm>

using namespace std;

int main()
{
    int T;
    int n;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%d",&n);
        int cnt=0;
        int temp;
        temp=n/5;
        while(temp>0)
        {
            cnt+=temp;
            temp/=5;
        }
        printf("%d\n",cnt);
    }
    return 0;
}

[HDU 4045] Machine scheduling

題目描述

A Baidu’s engineer needs to analyze and process large amount of data on machines every day. The machines are labeled from 1 to n. On each day, the engineer chooses r machines to process data. He allocates the r machines to no more than m groups ,and if the difference of 2 machines' labels are less than k,they can not work in the same day. Otherwise the two machines will not work properly. That is to say, the machines labeled with 1 and k+1 can work in the same day while those labeled with 1 and k should not work in the same day. Due to some unknown reasons, the engineer should not choose the allocation scheme the same as that on some previous day. otherwise all the machines need to be initialized again. As you know, the initialization will take a long time and a lot of efforts. Can you tell the engineer the maximum days that he can use these machines continuously without re-initialization.

有n臺機器,從1~n標號。每一天,選出r臺機器,並將其分爲m組。在同一組內的機器需知足:任意兩臺機器的序號差需不小於k。問:有多少種不一樣的分配機器的方法。

輸入

Input end with EOF.
Input will be four integers n,r,k,m.We assume that they are all between 1 and 1000.

輸出

Output the maxmium days modulo 1000000007.

樣例數據

樣例輸入 樣例輸出
5 2 3 2 6

 

 

解析

問題由兩部分構成:第一,從N個機器中選出R個知足條件的機器的方案數;第二,將R個機器最多分爲M組有的方案數。兩者乘積即爲答案。
第一部分:
先知足每兩個機器之間至少有K-1個間隔,也就是還剩下rem=n-((r-1)*k+1)個機器能夠隨意安排,把這些多餘的插入到R個機器之間(加上兩端共R+1個位置)。問題也就變爲rem個相同的球分到R+1個不一樣的組能夠爲空這種模型,不難推出是C(rem+R,R),可直接用插板法公式。
第二部分:
R個元素分爲i個非空集合是第二類斯特林數,對i爲1至m求和便可。

[不熟悉第二類Stirling數?]

Code

#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<iostream>

#define min(a,b) ((a)<(b)?(a):(b))
#define ll long long

const int mod=1000000007;
const int N=1005;

int C[2*N][2*N];
ll stir2[N][N];

void Init()
{
    for(int i=1;i<=2000;i++)
    {
        C[i][0]=C[i][i]=1;
        for (j=1;j<i;j++)
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%mod;
    }
    for(int i=1;i<=1000;i++)
    {
        stir2[i][0]=0;
        stir2[i][i]=1;
        for(int j=1;j<i;j++)
            stir2[i][j]=(stir2[i-1][j-1]+j*stir2[i-1][j])%mod;
    }
}

int main()
{
    Init();
    int n,m,k,r;
    while(~scanf("%d%d%d%d",&n,&r,&k,&m))
    {
        int rem=n-((r-1)*k+1);
        if(rem<0)
        {
            printf("0\n");
            continue;
        }
        ll sum=0,ans=C[rem+r][r];
        for (int i=1;i<=m;i++)
            sum=(sum+stir2[r][i])%mod;
        ans=(ans*sum)%mod;
        printf("%I64d\n",ans);
    }
    return 0;
}

[HDU 4532] 湫秋系列故事――安排座位

題目描述

爲了給騰訊公司找到更多優秀的人才,HR湫秋最近去某高校組織了一次針對該校全部系的聚會,邀請了每一個系的一些優秀學生來參加。
做爲組織者,湫秋要安排他們的座位。這並非一件很簡單的事情,由於只有一排位置,而且位置總數剛好等於參加聚會的人數。爲了促進交流,兩個來自相同系的同窗不能夠座位相鄰。湫秋如今但願知道有多少種不一樣的合理安排座位的方法(任意兩個合理的安排方法,只要有一個位置的同窗不一樣,都被認爲是不一樣的)。

輸入

輸入第一行爲T,表示有T組測試數據。
每組數據一個N開始,表示一共有多少個系。下面的一行包含N個整數Ai,表示每一個系的到場人數。
[Technical Specification]
1. 1 <= T <= 47
2. 1 <= N, Ai <= 47
3. 1 <= Sum(Ai) <= 447

輸出

對每組數據,先輸出爲第幾組數據,而後輸出結果。因爲結果可能很大,輸出對1,000,000,007 取餘後的結果。

樣例數據

樣例輸入 樣例輸出

3

2

1 2

2

1 3

3

1 2 3

Case 1: 2

Case 2: 0

Case 3: 120

 

 

 

 

 

 

 

 

 

解析

參見[將狼踩盡-博客園]

因爲每一個班級的人是不一樣的,咱們先將全部人看做相同的,最後乘以一個全排列便可。用f[i][j]表示前i個班級的人排好後有j個位置兩側是同一個班級的人的排列方案數,而後枚舉第i個分紅幾塊k 有l塊塞入j個不合法空隙中進行轉移。(具體見代碼)

Code

#include<cstdio>  
#include<iostream>
#include<cstring>  
#include<algorithm>

#define ll long long  
#define m 1000000007  
 
using namespace std;  

ll dp[50][480];   
ll C[500][500];  
ll A[500];      
int a[50];  

int main()
{  
    int t,n;  
    C[0][0]=1;  
    for(int i=1;i<480;i++)
    {  
        C[i][0]=1;  
        for(int j=1;j<i;j++)  
            C[i][j]=(C[i-1][j-1]+C[i-1][j])%m;  
        C[i][i]=1;  
    }  
    A[0]=A[1]=1;  
    for(int i=2;i<480;i++)  
        A[i]=(A[i-1]*i)%m;
    scanf("%d",&t);  
    for(int ca=1;ca<=t;ca++)
    {  
        scanf("%d",&n);  
        for(int i=1;i<=n;i++)  
            scanf("%d",&a[i]);  
        memset(dp,0,sizeof(dp));  
        dp[1][a[1]-1]=1;
        ll sum=a[1]; 
        //每次枚舉以前一共有j個空位置
        //將當前的d[i]分紅k組:C[a[i]-1][k-1]
        //在j個空位置中插入k組中的u組:C[j][u]
        //還剩下k-u組,插入到sum+1-j個空位置
        //注意:j表示一共有j個位置兩側是同一個班級的人
        //一共有sum我的,那麼還有sum+1-j位置兩側是不一樣班級的人
        for (int i=2;i<=n;i++)
        {  
            for(int j=0;j<sum;j++)
                for(int k=1;k<=a[i];k++) 
                    for(int u=0;u<=j&&u<=k;u++)
                        dp[i][j-u+a[i]-1-(k-1)]=(dp[i][j-u+a[i]-k]+(((dp[i-1][j]*C[j][u])%m*C[sum+1-j][k-u])%m*C[a[i]-1][k-1])%m)%m;  
            sum+=a[i];
        }
        printf("Case %d: ",ca);  
        ll ans=dp[n][0];
        for(int i=1;i<=n;i++)
            ans=(ans*A[a[i]])%m;  
        printf("%lld\n",ans);  
    }
    return 0;
}

 

[HDU 4810] Wall Painting

題目描述

Ms.Fang loves painting very much. She paints GFW(Great Funny Wall) every day. Every day before painting, she produces a wonderful color of pigments by mixing water and some bags of pigments. On the K-th day, she will select K specific bags of pigments and mix them to get a color of pigments which she will use that day. When she mixes a bag of pigments with color A and a bag of pigments with color B, she will get pigments with color A xor B.
When she mixes two bags of pigments with the same color, she will get color zero for some strange reasons. Now, her husband Mr.Fang has no idea about which K bags of pigments Ms.Fang will select on the K-th day. He wonders the sum of the colors Ms.Fang will get with different plans.
For example, assume n = 3, K = 2 and three bags of pigments with color 2, 1, 2. She can get color 3, 3, 0 with 3 different plans. In this instance, the answer Mr.Fang wants to get on the second day is 3 + 3 + 0 = 6.
Mr.Fang is so busy that he doesn’t want to spend too much time on it. Can you help him?
You should tell Mr.Fang the answer from the first day to the n-th day.

求n個數裏面,取i個數異或的全部組合的和,i取1~n。

輸入

There are several test cases, please process till EOF.
For each test case, the first line contains a single integer N(1 <= N <= 103).The second line contains N integers. The i-th integer represents the color of the pigments in the i-th bag.

輸出

For each test case, output N integers in a line representing the answers(mod 106 +3) from the first day to the n-th day.

樣例數據

樣例輸入 樣例輸出

4

1 2 10 1

14 36 30 8

 

 

 

 

解析

將n個數拆成30位2進制,因爲每一個二進制位異或後相加和原來的數異或相加是同樣的,因此只須要對每一位累加計算,用組合數學取數就好了,奇數個異或得1,偶數個異或得0,再乘以本身的二進制位值,複雜度O(30*n*n)

Code

#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
#include<algorithm>

#define LL long long
#define INF 0x3f3f3f3f

using namespace std;

const int maxn=1000+10;
const int mod=1000000+3;

LL c[maxn][maxn],a[35],p[35];

void init()
{
    memset(c,0,sizeof(c));
    for(int i=0;i<maxn-5;i++)
        c[i][i]=c[i][0]=1;
    for(int i=1;i<maxn-5;i++)
        for(int j=1;j<i;j++)
            c[i][j]=(c[i-1][j-1]+c[i-1][j])%mod;
    p[0]=1;
    for(int i=1;i<32;i++)
        p[i]=(2*p[i-1])%mod;
}

void cal(int x)
{
    int cnt=0;
    while(x)
    {
        if(x%2)
        a[cnt]++;
        x/=2;
        cnt++;
    }
}

int main()
{
    int n,x;
    LL tmp,ans;
    init();
    while(~scanf("%d",&n))
    {
        memset(a,0,sizeof(a));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&x);
            cal(x);
        }
        for(int i=1;i<=n;i++)
        {
            ans=0;
            for(int j=0;j<32;j++)
                for(int k=1;k<=i;k+=2)
                {
                    tmp=((LL)(p[j]*c[a[j]][k]*c[n-a[j]][i-k]))%mod;
                    ans+=tmp;
                    ans%=mod;
                }
            if(i==n)
                printf("%I64d\n",ans);
            else
                printf("%I64d ",ans);
        }
    }
    return 0;
}

 

Time: 2017-07-08

相關文章
相關標籤/搜索