Luogu P3266 [JLOI2015]骗我呢

Link
观察到每行只能是在\([0,m]\)中选择一个不出现的数,然后剩下的升序排列。
假如第\(i\)行不出现的数是\(j\),那么\(i-1\)行不出现的数必须是\([0,j-1]\)
因此我们可以设计一个dp,设\(f_{i,j}\)表示只考虑前\(i\)行,第\(i\)行不出现的数是\(j\)的方案数,那么显然有:
\(f_{i,j}=\sum\limits_{k=0}^{j+1}f_{i-1,k}=f_{i,j-1}+f_{i-1,j+1}\)(为了方便转移\(j\)的上界是\(m+1\))。
这样初始状态就是\(f_{0,0}=1\),答案就是\(f_{n,m+1}\)
考虑转化为格路问题,\(f_{n,m+1}\)就是从\((0,0)\)走到\((n,n+m+1)\),每次只能向上或向右走,不能碰到\(l_1:x-y=0,l_2:x-y+m+1=0\)两条直线的方案数。
再设\(f(x,y),g(x,y)\)表示先碰到\(l_1,l_2\)到达\((x,y)\)的方案数。
利用补集容斥得到\(f_{n,m+1}={2n+m+1\choose n}-(f(n,n+m+1)+g(n,n+m+1))\)
考虑经典做法对称,\(f(n,n+m+1)=f(n+m+2,n-1),g(n,n+m+1)=g(n-1,n+m+2)\)
此时\(f(x,y),g(x,y)\)一定满足\(y-x<0\vee y-x>m+1\),因此\((0,0)\)\((x,y)\)一定会越过边界,即\(f(x,y)+g(x,y)={x+y\choose x}\)
再次对称可以得到\(f(x,y)={x+y\choose x}-g(y-m-2,x+m+2),g(x,y)={x+y\choose x}-f(y+1,x-1)\)
这样就能保证递归求解时\(f(x,y),g(x,y)\)都满足\(y-x<0\vee y-x>m+1\)了。
边界条件为\(f(x,y)=g(x,y)=0\qquad\text{where}\quad x<0\vee y<0\)

#include<cstdio>
const int P=1000000007;
int n,m,fac[3000007],ifac[3000007];
int dec(int a,int b){return a-=b,a+=a>>31&P;}
int mul(int a,int b){return 1ll*a*b%P;}
int pow(int a,int k){int r=1;for(;k;k>>=1,a=mul(a,a))if(k&1)r=mul(a,r);return r;}
int C(int n,int m){return mul(mul(fac[n],ifac[m]),ifac[n-m]);}
int f(int,int);int g(int,int);
int f(int x,int y){return x<0||y<0? 0:dec(C(x+y,x),g(y-m-2,x+m+2));}
int g(int x,int y){return x<0||y<0? 0:dec(C(x+y,x),f(y+1,x-1));}
int main()
{
    scanf("%d%d",&n,&m),fac[0]=1;
    for(int i=1;i<=2*n+m+1;++i) fac[i]=mul(fac[i-1],i);
    ifac[2*n+m+1]=pow(fac[2*n+m+1],P-2);
    for(int i=2*n+m+1;i;--i) ifac[i-1]=mul(ifac[i],i);
    printf("%d",dec(dec(C(2*n+m+1,n),f(n+m+2,n-1)),g(n-1,n+m+2)));
}
posted @ 2020-02-02 17:23  Shiina_Mashiro  阅读(189)  评论(0编辑  收藏  举报