【CF627E】Orchestra

题目

在这里插入图片描述

思路

最暴力的方法就是枚举上下边界,然后单调队列来统计。时间复杂度 O ( n 3 ) O(n^3) O(n3)

反过来考虑,从下往上枚举每一行 先算出以这一行的每个顶点为左上角,最下面一行为第cc的所有合法矩阵个数 计算方法考虑尺取即可 再考虑最下面一行逐渐往上走的合法矩阵个数

这个过程中,点是在不断变少的 考虑算出将最下面一行变为其上面那一行时合法矩阵的减少量,再用原来的合法矩阵量减去就是最下面一行往上移一行后的合法矩阵数量

如何计算合法矩阵的减少量 枚举这一行中的每个点,计算出包含这个点且点数刚好为kk的矩阵个数,然后删去这个点 计算方法也是尺取,另外,尺取时用一个链表表示该行中下一个点和上一个点的位置,这样尺取复杂度就是该行点数,而不是mm 这样就能算出删去这一行中所有点后不合法的矩阵个数 那么往上移一行合法矩阵就会减去这些矩阵

时间复杂度: O ( n 2 k ) O(n^2k) O(n2k)

代码

#include<bits/stdc++.h>
#define ll long long	
using namespace std;	
const int N=3077;	
int n,m,p,k,s[N],t[N],ls[N],nxt[N];
ll ans;
vector<int> P[N];	
int main()	
{
	scanf("%d%d%d%d",&n,&m,&p,&k);
	for(int i=1; i<=p; i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		P[x].push_back(y);
	}
	for(int u=n; u>=1; u--)
	{
		for(int i=0; i<P[u].size(); i++) ++s[P[u][i]];
		int l=1,r=0,yjy=0;
		ll aii=0;
		while(1)
		{
			while(r+1<=m&&yjy<k) yjy+=s[++r];
			if(yjy<k) break;
			while(l<=r&&yjy>=k) yjy-=s[l++],aii+=m-r+1;
		}
		for(int i=1; i<=m; i++) t[i]=s[i];
		for(int i=0; i<=m+1; i++) ls[i]=i-1,nxt[i]=i+1;
		for(int i=1; i<=m; i++) if(!s[i]) ls[nxt[i]]=ls[i],nxt[ls[i]]=nxt[i];
		for(int d=n; d>=u; d--)
		{
			ans+=aii;
			for(int i=0; i<P[d].size(); i++)
			{
				int s=P[d][i],sl=t[s],sr=0;
				l=r=s;
				while(nxt[r]<=m&&sl+sr+t[nxt[r]]<=k) sr+=t[r=nxt[r]];
				while(1)
				{
					if(sl+sr==k) aii-=(l-ls[l])*(nxt[r]-r);
					sl+=t[l=ls[l]];
					if(!l||sl>k) break;
					while(sl+sr>k) sr-=t[r],r=ls[r];
				}
				--t[s];
				if(!t[s]) ls[nxt[s]]=ls[s],nxt[ls[s]]=nxt[s];
			}
		}
	}
	printf("%lld",ans);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值