【题意】 给出r行c列的个子矩阵, 给k个点。 求第一行, 最后一行, 第一列, 最后一列都放有点的方法数
A 表示第一行没有放的集合
B 最后一行没有放的集合
C 第一例没有放的集合
D 最后一列没有放的集合
在A,B里 相当一 少了一行 其方法数是 C[(r-1)*c][k] C,D里相当于 少了一列 其方法数是C[(r*(c-1))][k]
ans= 所有情况 -A∪B∪C∪D
根据容斥原理:
A∪B∪C∪D=A+B+C+D- A∩B - B∩C - C∩A- A∩D - B∩D - C∩D+A∩B∩C
+A∩B∩D +A∩C∩D +B∩C∩D -A∩B∩C∩D
注意A∪B∪C∪D 前有一个 ‘ - ’号 当相交的集合数位奇数时 前面的系数是 - 为偶数时前面的系数是 +
可以看到总共的项数是 C(4,1)+C(4,2)+C(4,3)+C(4,4)=2^4-1;
C(N,M) 为总共有n个集合中选任意m个集合做交集 的个数
例如 A∩B∩C∩D表示为1111 A∩C∩D 表示为1011 A∩B表示为1100 A表示为1000
#include<cstdio> #include<cstring> using namespace std; #define maxx 22 #define setnum 4 int C[maxx][maxx]; int n,m; void init() { memset(C,0,sizeof(C)); for(int i=0;i<maxx;i++) C[i][0]=C[i][i]=1; for(int i=2;i<maxx;i++) for(int j=1;j<=i;j++) C[i][j]=C[i-1][j-1]+C[i-1][j]; } int main() { init(); int k,i,j; while(~scanf("%d%d%d",&n,&m,&k)) { int ans=0; for(int i=0;i<(1<<setnum);i++) { int r=n,c=m; int num=0; if(i&1) { r--;num++; } if(i&2) { r--;num++; } if(i&4) { c--;num++; } if(i&8) { c--;num++; } if(num%2) ans-=C[r*c][k]; else ans+=C[r*c][k]; } printf("%d\n",ans); } return 0; }