1. 程式人生 > >【洛谷P1641】[SCOI2010]生成字符串

【洛谷P1641】[SCOI2010]生成字符串

個數 ati es2017 name span thml www 生成 n)

題目描述

lxhgww最近接到了一個生成字符串的任務,任務需要他把n個1和m個0組成字符串,但是任務還要求在組成的字符串中,在任意的前k個字符中,1的個數不能少於0的個數。現在lxhgww想要知道滿足要求的字符串共有多少個,聰明的程序員們,你們能幫助他嗎?

輸入輸出格式

輸入格式:

輸入數據是一行,包括2個數字n和m

輸出格式:

輸出數據是一行,包括1個數字,表示滿足要求的字符串數目,這個數可能會很大,只需輸出這個數除以20100403的余數

輸入輸出樣例

輸入樣例#1:
2 2
輸出樣例#1:
2

說明

limitation

每點2秒

對於30%的數據,保證1<=m<=n<=1000

對於100%的數據,保證1<=m<=n<=1000000

來源:SCOI 2010

分析(轉載)

原文地址

寫的確實不錯。

技術分享

首先,我們設選1為(1,1),選0為(1,-1)

目標就是(n+m,n-m)

總方案數為C(n+m,n),因為有n+m個位置,放n個1

然後要減去不合法的即線路通過y=-1的。將線路與y=-1交點的左邊沿著y=-1做對稱操作,則最後等價於從(0,-2)走到(n+m,n-m)的方案數

所以向上走n-m+2

則有x-y=n-m+2

  x+y=n+m

  x=n+1,y=m-1

所以不合法方案為C(n+m,n+1)

ans=C(n+m,n)-C(n+m,n+1)

求這些用模逆元,O(n)求解

另附上洛谷題解中的分析

原文地址

可以考慮把11的個數與00的個數的和看成xx坐標,11的個數與00的個數的差看成yy坐標,那麽如下圖:

技術分享

向右上走(xx坐標加11,yy坐標加11)就表示這個字符選擇11。

向右下走(xx坐標加11,yy坐標減11)就表示這個字符選擇00。

這樣子,如果不考慮限制條件,就表示從(0,0)(0,0)走n+mn+m步到達(n+m,n-m)(n+m,n?m),這相當於從n+mn+m步中選出mm步向右下走,也就是C(n+m,m)C(n+m,m)。

考慮限制條件,任意前綴中11的個數不少於00的個數,也就是這條路徑不能經過直線y=-1y=?1。可以通過對稱性發現,從(0,0)(0,0)走到直線y=-1y=?1上的一點,相當於從(0,-2)(0,?2)走到該點。也就是說,路徑經過直線y=-1y=?1的方案數就是從(0,-2)(0,?2)走n+mn+m步到達(n+m,n-m)(n+m,n?m),這個方案數可以用組合數表示為C(n+m,m-1)C(n+m,m?1)。

所以最後結果為C(n+m,m)-C(n+m,m-1)C(n+m,m)?C(n+m,m?1)。對於組合數,可以預處理階乘後用乘法逆元計算。

代碼

我是用費馬小定理做的

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int p=20100403;
typedef long long ll;
int n,m;
int fac[2000005];
int power(int a,int b)
{
    int res=1,base=a;
    while(b)
    {
        if(b&1) res=(ll)res*(ll)base%(ll)p;
        base=(ll)base*(ll)base%(ll)p;
        b=b>>1;
    }
    return res;
}
int inv(int x)
{ return power(x,p-2);}
void fct()
{
    fac[0]=1;
    for(int i=1;i<=2000000;i++)
        fac[i]=(ll)fac[i-1]*(ll)i%(ll)p;
}
int main()
{
    scanf("%d%d",&n,&m);
    fct();
    int t1,t2,x1,x2,ans;
    t1=fac[n+m];
    t2=(ll)fac[n]*(ll)fac[m]%(ll)p;
    x1=(ll)t1*(ll)inv(t2)%(ll)p;
    t2=(ll)fac[m-1]*(ll)fac[n+1]%(ll)p;
    x2=(ll)t1*(ll)inv(t2)%(ll)p;
    ans=(x1%p-x2%p+p)%p;
    printf("%d\n",ans);
    return 0;
}
    

【洛谷P1641】[SCOI2010]生成字符串