CF1396D Rainbow Rectangles

一、题目

点此看题

二、解法

考虑如果这个问题放在一维上是具有单调性的,尺取法可以做到 \(O(n)\)

我们可以通过枚举上下边界把它变成一维问题,从左往右扫可以做到 \(O(n^3)\)

我们考虑优化掉边界枚举的过程,也就是我们固定上边界,移动下边界。设 \(f_l\) 表示在上下边界中 \(y=l\) 的最小右端点,我们需要在移动边界时动态维护这东西。

如果我们把下边界从上往下扫,那么会产生一个增加点的问题,但是增加点对于 \(f\) 的影响是不好考虑的,因为我们不知道它是怎么变小的。

考虑把下边界从下往上扫,那么会产生一个删除点的问题,设这个点的同色前驱(定义为边界内 \(y\) 比它不比他大的最大点)为 \(pre\),后继(定义类似)为 \(nxt\),那么会把 \(j\in(pre,i]\)\(f_j\leftarrow\max(f_j,nxt)\)

因为 \(f\) 具有单调性,所以可以用线段树转化为维护区间赋值,时间复杂度 \(O(n^2\log n)\)

离散化的部分真的不好讲,这个东西需要你去实现然后自行体会,可以参考优美实现:

三、总结

为什么二维问题我只会扫描线?其实常规模型是很多的。比如计算方形可以使用二维 \(\tt st\) 表;计算矩形可以考虑枚举边界转一维问题;或者是中线分治,考虑过中线的矩形(说实话这个我现在不是很会)

不一定只有增加好做,比如这题就是删除好做,你主要看怎么做会产生易于维护的限制,这才是数据结构题的关键。

#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
const int M = 2005;
const int N = 8005;
const int MOD = 1e9+7;
#define int long long
int read()
{
	int x=0,f=1;char c;
	while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;}
	while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();}
	return x*f;
}
int n,m,k,f[M],c[M],ys[M],ls[M],col[M];
int ans,sum[N],mi[N],mx[N],tag[N],vis[M];
struct table
{
	int l[M],r[M];
	void del(int x) {l[r[x]]=l[x];r[l[x]]=r[x];}
}nl,li;
struct node {int x,y,c,id;}a[M];
bool cmpx(node a,node b) {return a.x<b.x;}
bool cmpy(node a,node b) {return a.y<b.y;}
void fuck(int i,int l,int r,int c)
{
	mi[i]=mx[i]=tag[i]=c;
	sum[i]=(ys[r]-ys[l-1])*c;
}
void down(int i,int l,int r)
{
	if(!tag[i]) return ;
	int mid=(l+r)>>1;
	fuck(i<<1,l,mid,tag[i]);
	fuck(i<<1|1,mid+1,r,tag[i]);
	tag[i]=0;
}
void up(int i)
{
	mx[i]=max(mx[i<<1],mx[i<<1|1]);
	mi[i]=min(mi[i<<1],mi[i<<1|1]);
	sum[i]=sum[i<<1]+sum[i<<1|1];
}
void build(int i,int l,int r)
{
	tag[i]=0;
	if(l==r)
	{
		fuck(i,l,l,f[l]);
		return ;
	}
	int mid=(l+r)>>1;
	build(i<<1,l,mid);
	build(i<<1|1,mid+1,r);
	up(i);
}
void add(int i,int l,int r,int L,int c)
{
	if(L>r || mi[i]>=c) return ;
	if(L<=l && mx[i]<=c)
	{
		fuck(i,l,r,c);
		return ;
	}
	int mid=(l+r)>>1;down(i,l,r);
	add(i<<1,l,mid,L,c);
	add(i<<1|1,mid+1,r,L,c);
	up(i);
}
signed main()
{
	n=read();k=read();m=read();
	for(int i=1;i<=n;i++)
		a[i].x=read(),a[i].y=read(),a[i].c=read();
	sort(a+1,a+1+n,cmpy);
	a[0].x=a[0].y=-1;a[n+1].x=a[n+1].y=m;
	for(int i=0;i<=n+1;i++) ys[i]=a[i].y,a[i].id=i;
	for(int i=1;i<=n;i++)
	{
		int l=ls[a[i].c];ls[a[i].c]=i;
		nl.r[l]=i;nl.l[i]=l;nl.r[i]=n+1;
	}
	sort(a+1,a+1+n,cmpx);
	for(int i=1;i<=n;nl.del(a[i].id),i++)
	if(a[i].x^a[i-1].x)
	{
		for(int j=1;j<=k;j++) vis[j]=0;
		for(int j=1;j<i;j++) c[a[j].id]=0;
		for(int j=i;j<=n;j++) c[a[j].id]=a[j].c;
		for(int l=1,r=1,tot=0;l<=n;l++)
		{
			for(;tot<k && r<=n;r++) if(c[r])
				vis[c[r]]++,tot+=(vis[c[r]]==1);
			f[l]=(tot<k)?m:ys[r-1];
			if(c[l]) vis[c[l]]--,tot-=!vis[c[l]];
		}
		build(1,1,n);li=nl;
		for(int j=n;j>=i;j--)
		{
			ans+=((ys[n]+1)*m-sum[1])%MOD*
			(a[i].x-a[i-1].x)%MOD
			*(a[j+1].x-a[j].x)%MOD,ans%=MOD;
			int x=a[j].id,l=li.l[x],r=li.r[x];
			add(1,1,n,l+1,ys[r]);li.del(x);
		}
	}
	printf("%lld\n",ans);
}
posted @ 2021-10-22 11:07  C202044zxy  阅读(77)  评论(0编辑  收藏  举报