CF634F Orchestra

CF634F Orchestra

  • 给一个 r ∗ c r*c rc的矩形,其中有 n n n个位置为1,第 i i i个位置为 ( x i , y i ) (x_i,y_i) (xi,yi),其它地方为0。
  • 求有多少个子矩形使得矩形和不小于 k k k
  • r , c , n ≤ 3000 , 1 ≤ k ≤ m i n ( n , 10 ) r,c,n\le3000,1\le k\le min(n,10) r,c,n3000,1kmin(n,10)

Solution

  • 考虑对于一个矩形 ( x 1 , x 2 , y 1 , y 2 ) (x1,x2,y1,y2) (x1,x2,y1,y2)固定 y 1 y1 y1 y 2 y2 y2从大往小枚举,对于每一个 x 1 x1 x1维护一个最大的 x 2 x2 x2使得 s u m ( x 1 , x 2 , y 1 , y 2 ) < k sum(x1,x2,y1,y2)<k sum(x1,x2,y1,y2)<k
  • 由于 k ≤ 10 k\le10 k10,所以可以考虑离散化之后暴力修改影响得到的 x 2 x2 x2,如果离散以后每一行都至少有1个,那么这样暴力改到的部分一定不会超过 k k k个。
  • y 2 y2 y2从大往小的时候在链表中不断删掉在这一列的元素即可简单地实现离散化。
#include<cstdio>
#include<cmath>
#include<cstring>
#include<algorithm>
#define maxn 3005
#define ll long long 
using namespace std;

int n,m,p,lim,i,j,k,x[maxn],tot;
int bz[maxn],c[maxn],g[maxn],pre[maxn],nex[maxn];
ll f[maxn],ans,sum;
struct node{int x,y;} a[maxn];
int cmp1(node a,node b){return a.x<b.x;}
int cmp2(node a,node b){return a.y<b.y;}
ll Mul(ll x){return x*(x+1)/2;}
void getf(int i){
	if (i==0||i==tot+1) {f[i]=0;return;}
	f[i]=Mul(x[i]-1-x[pre[i]]);
	if (c[i]<=lim) f[i]+=(ll)(x[nex[g[i]]]-x[i])*(x[i]-x[pre[i]]);	
}

int main(){
	freopen("ceshi.in","r",stdin);
	scanf("%d%d%d%d",&n,&m,&p,&lim),lim--;
	for(i=1;i<=p;i++) scanf("%d%d",&a[i].x,&a[i].y);
	sort(a+1,a+1+p,cmp1);
	for(i=1;i<=p;i++) {
		if (i==1||a[i].x!=a[i-1].x) 
			x[++tot]=a[i].x;
	} x[tot+1]=n+1;
	for(i=1,j=1;i<=p;i++){
		while (x[j]<a[i].x) j++;
		a[i].x=j;
	}
	sort(a+1,a+1+p,cmp2);
	for(int L=1;L<=m;L++){
		for(i=p;i>=1;i--) if (a[i].y>=L) bz[a[i].x]++; else break;
		k=0;for(i=1;i<=tot;i++) if (bz[i]) pre[i]=k,nex[k]=i,k=i;
		pre[tot+1]=k,nex[k]=tot+1,sum=0;
		for(i=nex[0];i!=tot+1;i=nex[i]){
			g[i]=i,c[i]=bz[i];
			while (nex[g[i]]!=tot+1&&c[i]+bz[nex[g[i]]]<=lim) 
				g[i]=nex[g[i]],c[i]+=bz[g[i]];
			getf(i),sum+=f[i];
		}
		k=p;
		for(int R=m;R>=L;R--){
			ans+=sum+Mul(n-x[pre[tot+1]]);
			for(;k&&a[k].y==R;k--) {
				bz[a[k].x]--;
				for(i=a[k].x;i&&g[i]>=a[k].x;i=pre[i]){
					sum-=f[i],c[i]--;
					while (nex[g[i]]!=tot+1&&c[i]+bz[nex[g[i]]]<=lim)
						g[i]=nex[g[i]],c[i]+=bz[g[i]];
					getf(i),sum+=f[i];
				}
				for(;i&&c[i]+bz[nex[g[i]]]<=lim;i=pre[i]){
					sum-=f[i];
					while (nex[g[i]]!=tot+1&&c[i]+bz[nex[g[i]]]<=lim)
						g[i]=nex[g[i]],c[i]+=bz[g[i]];
					getf(i),sum+=f[i];
				}
				if (!bz[a[k].x]){
					sum-=f[pre[a[k].x]]+f[nex[a[k].x]]+f[a[k].x];
					for(i=pre[a[k].x];g[i]>a[k].x;i=pre[i]);
					while (g[i]==a[k].x) g[i]=pre[a[k].x],i=pre[i];
					nex[pre[a[k].x]]=nex[a[k].x];
					pre[nex[a[k].x]]=pre[a[k].x];
					getf(pre[a[k].x]);
					getf(nex[a[k].x]);
					sum+=f[pre[a[k].x]]+f[nex[a[k].x]];
				}
			}
		}
	}
	printf("%lld",Mul(n)*Mul(m)-ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值