版权声明:本文为蒟蒻原创文章,转载请注明出处哦~ 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;
}