1. 程式人生 > >[Luogu P1641] [BZOJ 1856] [SCOI2010]生成字串

[Luogu P1641] [BZOJ 1856] [SCOI2010]生成字串

洛谷傳送門

題目描述

lxhgww最近接到了一個生成字串的任務,任務需要他把nn11mm00組成字串,但是任務還要求在組成的字串中,在任意的前kk個字元中,11的個數不能少於00的個數。現在lxhgww想要知道滿足要求的字串共有多少個,聰明的程式設計師們,你們能幫助他嗎?

輸入輸出格式

輸入格式:

輸入資料是一行,包括22個數字nnmm

輸出格式:

輸出資料是一行,包括11個數字,表示滿足要求的字串數目,這個數可能會很大,只需輸出這個數除以2010040320100403的餘數

輸入輸出樣例

輸入樣例#1:

2 2

輸出樣例#1:

2

說明

limitation

每點22

對於30

%30\%的資料,保證1mn10001\le m\le n\le 1000

對於100%100\%的資料,保證1mn10000001\le m\le n\le 1000000

來源:SCOI 2010

解題分析

可以抽象為(0,0)(0,0)點走到(n,m)(n,m)點, 只能向右或向上, 但不能經過y=x+1y=x+1這條直線, 如下圖:

img

可以做一個對稱點A(1,1)A'(-1,1), 其到BB點的任何一條合法路徑都會經過y=x+1y=x+1,即不合法方案, 所以合法方案即為(n+mm)(n+mm1)\binom{n+m}{m}-\binom{n+m}{m-1}

, 線篩逆元即可。

程式碼如下:

#include <cstdio>
#include <cstring>
#include <cstdlib>
#include <cctype>
#include <algorithm>
#include <cmath>
#define R register
#define IN inline
#define W while
#define gc getchar()
#define MX 2000500
#define ll long long
#define MOD 20100403
int fac[MX], inv[MX], sum[MX]; int n, m; IN int C(R int n, R int m) {return 1ll * fac[n] * sum[m] % MOD * sum[n - m] % MOD;} int main(void) { inv[0] = inv[1] = fac[1] = sum[1] = 1; scanf("%d%d", &n, &m); int bd = n + m; for (R int i = 2; i <= bd; ++i) fac[i] = 1ll * fac[i - 1] * i % MOD, inv[i] = 1ll * (MOD - MOD / i) * inv[MOD % i] % MOD; for (R int i = 2; i <= bd; ++i) sum[i] = 1ll * sum[i - 1] * inv[i] % MOD; printf("%d", (C(n + m, m) - C(n + m, m - 1) + MOD) % MOD); }