Reverse It(组合数学)

原题链接
Problem Description
Taotao is a boy who is addicted to cards. He comes up with a card-related problem. As usual, Taotao can’t solve the problem independently. Thus, he asks your help.
Taotao places n rows and m columns of cards on the desktop. Each card may be face up or it may be reverse side up. Taotao can perform at most two operations. He selects a rectangle of any size in the desktop for each operation and reverses cards in the rectangle. And he wants to know how many different types of card placement he can get. Two card placements are regarded as different only if there is at least one card in the same position reversed.
Input
There are multiple test cases.
For each test case, first line contains two integers n, m(1<=n, m<=100).
The next n lines each contain m digitals. ‘1’ represents the card is face up. ‘0’ represents the card is reverse side up.
Output
For each test case, you should output one integer represents the number of different card placements.
Sample Input
1 2
10
Sample Output
4
题中说最多只能翻两次,翻一次有 C n + 1 2 ∗ C m + 1 2 C_{n+1}^{2}*C_{m+1}^{2} Cn+12Cm+12种方式(记为 k k k),翻两次有 C k 2 C_{k}^{2} Ck2中方式,而不翻只有一种,就是初始状态。翻两次包含了翻一次的情况,所以可以不用考虑翻一次的情况,在 C k 2 + 1 C_{k}^{2}+1 Ck2+1种方式中,得到的结果可能重复,这是就要去重。
1.如果两个选区中心对称的话,就要减去一次。
2.如果一个分区分割了另一个分区,这时也要减去一次。
3.如果两个分区有切点或切边,并且两个分区的大小相等,也要减去一次。

#include
#include
typedef long long ll;
const int MAXN=1e2+5;
ll n,m,c[MAXN][MAXN];
char s[105];
int main()
{
    memset(c,0,sizeof(c));
    for(int i=0;i<MAXN;i++) c[i][0]=c[i][i]=1;
    for(int i=0;i<MAXN;i++)
        for(int j=1;j<i;j++)
            c[i][j]=c[i-1][j-1]+c[i-1][j];
    while(~scanf("%lld%lld",&n,&m))
    {
        for(int i=0;i<n;i++) scanf("%s",s);
        ll res=c[n+1][2]*c[m+1][2];
        res=res*(res-1)/2+1;
        res-=4*c[n+1][3]*c[m+1][3];//对角
        res-=8*c[n+1][3]*c[m+1][3];//斜率
        res-=2*c[n+1][2]*c[m+1][4]+2*c[n+1][4]*c[m+1][2];//分割
        res-=(n+m-3)*c[n+1][2]*c[m+1][2];
        printf("%lld\n",res);
    }
    return 0;
}

你可能感兴趣的:(Reverse It(组合数学))