Gym - 101350G Snake Rana (容斥)

版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ https://blog.csdn.net/a54665sdgf/article/details/82625124

题目链接

题意:给你一个n*m的矩阵,其中有k个格子里有地雷,求没有地雷的子矩阵数。(1<=n,m<=10000,k<=20)

解法:题目给的n和m范围较大,直接枚举不合适。但是给的k的范围却很小,因此可以考虑从k入手。

根据容斥原理,没有地雷的子矩阵数=全部子矩阵数-包含一颗地雷的子矩阵数+包含两颗地雷的子矩阵数-包含三颗地雷的子矩阵数+...,因此可以枚举k个地雷的取法,用总数去减就行了。总复杂度为O(2^k)

总子矩阵数为n*(n+1)/2*m*(m+1)/2,即分别取两行和两列作为矩阵边界。

枚举集合可以用二进制编码的方法,也可以用递归,用递归应该可以更快一些。

#define FRER() freopen("i.txt","r",stdin)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int INF=0x3f3f3f3f;
const int N=20+5;
ll n,m,k;
ll ans;
ll x[N],y[N];

void select(int key)
{
    int cnt=0;
    ll add=0;
    ll L=INF,R=-1,T=INF,B=-1;
    for(int i=0;i<k;++i)
    {
        if(key&(1<<i))
        {
            L=min(L,x[i]);
            R=max(R,x[i]);
            T=min(T,y[i]);
            B=max(B,y[i]);
            cnt++;
        }
    }
    add=L*T*(n-R+1)*(m-B+1);
    if(cnt&1)add=-add;
    ans+=add;
}

int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        scanf("%lld%lld%lld",&n,&m,&k);
        for(int i=0;i<k;++i)scanf("%lld%lld",&x[i],&y[i]);
        ans=n*m*(n+1)*(m+1)/4;
        for(int key=1;key<(1<<k);++key)select(key);
        printf("%lld\n",ans);
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/a54665sdgf/article/details/82625124