Uva 11806 拉拉队 二进制+容斥原理 经典!

【题意】 给出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
",ans);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/assult/p/3757529.html