题意:给2*N的矩阵放满物品,共有M种物品,每种物品有无限个,同行列不能有相同种类的物品,问你有多少种放法。
思路:不用容斥原理也是可以解的,用对错排的递推式做出修改,以前的错排是N对N,现在多了M-N个,这里只介绍容斥原理做法。
容斥原理最重要的是处理好集合的关系,这个题目首先应该考虑把一行放满,有A(M,N)种,然后考虑下面的放法,要求同一列不能有一样的,我们正难则反,考虑下面有和上面一样的情况,令集合Ui表示至少有i个和上面一样,方案数是C(N,i)*A(M-i,N-i),那下面合理的办法共有U0 - U1 + U2 - U3……种,那么ans = 上 * 下
代码:
#include<stdio.h> #include<string.h> #include<algorithm> #include<math.h> #include<map> #include<queue> #include<iostream> using namespace std; #define LL long long const LL Mod = 1000000007; LL fac[1005],ni[1005]; LL qpow(LL x,int y){ LL res = 1; while(y){ if(y&1){ res=(res*x)%Mod; } x=(x*x)%Mod; y>>=1; } return res; } void init(){ fac[0] = 1; for(int i=1;i<=1002;i++){ fac[i] = (fac[i-1]*i)%Mod; } ni[1002] = qpow(fac[1002],Mod-2); for(int i=1002;i>=1;i--){ ni[i-1] = (ni[i]*i)%Mod; } } LL C(int n,int m){ if(m<0 || m>n) return 0; if(m==0 || m==n) return 1; LL res = (fac[n]*ni[m])%Mod; res = (res*ni[n-m])%Mod; return res; } LL A(int n,int m){ if(m<0 || m>n) return 0; LL res = (fac[n]*ni[n-m])%Mod; return res; } int main(){ init(); int T,n,m; scanf("%d",&T); while(T--){ scanf("%d%d",&n,&m); if(n > m){ printf("0\n"); continue; } LL up = A(m,n); LL down = 0; for(int i=0;i<=n;i++){ LL tmp = C(n,i)*A(m-i,n-i); tmp %= Mod; if(i&1) down = (down-tmp)%Mod; else down = (down+tmp)%Mod; } down = (down+Mod)%Mod; LL ans = (up*down)%Mod; printf("%lld\n",ans); } return 0; }