zoukankan      html  css  js  c++  java
  • CF1396D Rainbow Rectangles

    一、题目

    点此看题

    二、解法

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

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

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

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

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

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

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

    三、总结

    为什么二维问题我只会扫描线?其实常规模型是很多的。比如计算方形可以使用二维 ( t 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
    ",ans);
    }
    
  • 相关阅读:
    湾区求职分享:三个月刷题拿到 Google offer,欢迎踊跃提问
    【转】关于写书
    【转】真相
    【转】成都的雾霾
    【转】iPhone X
    【转】网络用语
    【转】AlphaGo Zero 和强人工智能
    【转】理性的力量
    【转】旅行的智慧
    【转】我为什么爱猫
  • 原文地址:https://www.cnblogs.com/C202044zxy/p/15437872.html
Copyright © 2011-2022 走看看