有一个N*M的矩形,矩形中有K块涂黑了,给出涂黑的坐标,问完全白色的矩形有几个。
根据数据范围的不同,有多种做法。
Gym101350G 容斥
,。
思路:
因为K比较小,N,M又比较大,所以可以考虑容斥。
对于一个N*M的矩形,它的子矩形有个,就相当于在横的边里任意选两条,竖着的边里任意选两条,相当于选出了矩形的边界。
根据容斥原理,那就要算出包含涂黑的矩形有几个,奇加偶减,对于包含奇数个黑块的矩形,就减,对于包含偶数个黑块的矩形,就加。
如何算出包含黑块的矩形有几个?
只要知道minr,minc,maxr,maxc就可以了。
就相当于选矩形的四条边,最左边的那条边有minc种选法,最右边的那条边有m-maxc+1种选法,最上边的那条边有minr种选法,最下边的那条边有n-maxr+1种选法。个数为。
所以二进制枚举容斥一下就可以了。
代码:
typedef long long ll;
#define INF 0x3f3f3f3f
struct point
{
int r,c;
}p[25];
int main()
{
int t,n,m,k;
scanf("%d",&t);
for(int kase=1;kase<=t;kase++)
{
scanf("%d%d%d",&n,&m,&k);
for(int i=0;i<k;i++)
scanf("%d%d",&p[i].r,&p[i].c);
ll tot=1ll*n*(n+1)/2*m*(m+1)/2;
//printf("%lld\n",tot);
int minr,minc,maxr,maxc;
for(int i=1;i<(1<<k);i++)
{
minr=INF;minc=INF;maxr=-INF;maxc=-INF;
int cnt=__builtin_popcount(i);
for(int j=0;j<k;j++)
{
if(i>>j&1)
{
minr=min(minr,p[j].r);
minc=min(minc,p[j].c);
maxr=max(maxr,p[j].r);
maxc=max(maxc,p[j].c);
}
}
//printf("%d %d %d %d\n",minr,minc,maxr,maxc);
if(cnt%2==1)tot-=1ll*minr*minc*(n-maxr+1)*(m-maxc+1);
else tot+=1ll*minr*minc*(n-maxr+1)*(m-maxc+1);
}
printf("Case #%d: %lld\n",kase,tot);
}
return 0;
}
南京网络赛B 单调栈
,,。
思路:
K的范围明显不能容斥。
首先对于矩形中的每一块可以处理出它向上可以选几条边。
固定下面那条边,枚举每一块,处理出它左右可以扩展到哪里,即左右各可以选几条边(在上面选的边都可用的情况下)。
以向左扩展为例,如果左边的高度比它高或跟它相同的话,就肯定可以继续扩展过去,但是如果出现高度比它低的情况的时候,就不能扩展了。所以相当于找它的左边第一个比它低的块,这里就可以用单调栈了。如果当前元素小于等于栈顶元素的话,就把栈顶弹掉。类似于HDU1506。
向右扩展的话,要注意高度和它相同的不能再扩展了,因为扩展的话后面的往左边扩展会算进去这种情况,会造成重复。
所以对于每个块,完全空白的矩形为。
代码:
typedef long long ll;
int g[100010][110];
int h[100010][110];
int l[110],r[110];
int main()
{
int t,n,m,k,x,y;
scanf("%d",&t);
for(int kase=1;kase<=t;kase++)
{
scanf("%d%d%d",&n,&m,&k);
memset(g,0,sizeof(g));
while(k--)
{
scanf("%d%d",&x,&y);
g[x][y]=1;
}
for(int i=1;i<=n;i++)
{
for(int j=1;j<=m;j++)
{
if(g[i][j]==1)h[i][j]=0;
else h[i][j]=h[i-1][j]+1;
}
}
ll ans=0;
for(int i=1;i<=n;i++)
{
stack<int>s;
for(int j=1;j<=m;j++)
{
l[j]=0;
while(!s.empty()&&h[i][j]<=h[i][s.top()])
s.pop();
if(!s.empty())l[j]=s.top();
s.push(j);
}
while(!s.empty())s.pop();
for(int j=m;j>=1;j--)
{
r[j]=m+1;
while(!s.empty()&&h[i][j]<h[i][s.top()])
s.pop();
if(!s.empty())r[j]=s.top();
s.push(j);
}
/*for(int j=1;j<=m;j++)
printf("i=%d j=%d:[%d %d]\n",i,j,j-l[j],r[j]-j);*/
for(int j=1;j<=m;j++)
ans+=1ll*h[i][j]*(j-l[j])*(r[j]-j);
}
printf("Case #%d: %lld\n",kase,ans);
}
return 0;
}
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!