CF1396 D. Rainbow Rectangles

在一个 L L L*L 方格(下标为 [ 0 , L ] [0,L] ).
n n M & M M\&M 豆,第 i i 个坐标为 ( x i + 0.5 , y i + 0.5 ) (x_i+0.5,y_i+0.5) ,颜色为 c i c_i ,总共有 m m 种.
问有多少个矩形能包含所有的颜色. ( n 3000 ) (n\le 3000) .

显然,我们可以把豆放在左下角,那么坐标范围改为 [ 0 , L ) [0,L) .
然后呢, n n 条横竖线把方格划分为 n n n*n 块,显然每两块之间的包含情况相同.(忽略边界)

暴力做法当然是 O ( n 4 ) O(n^4) 的枚举啦,这个想必大家都会,但是这个还有可能 M L E MLE .

换个思路,如果我们确定了两个 x x 坐标的范围,然后求有多少种合法的 y y 一样可以求解.
因为本质不同的 x x 可以视为 O ( n ) O(n) ,所以枚举 x x 的区间是 O ( n 2 ) O(n^2) .

我们对每一个本质相同的 y y 都求出一个上界 t t ,满足 i ( t , L ) , [ y , i ] \forall i\in (t,L),[y,i] 是一种合法的 y y 区间.
显然这个上界单调不降,每次 x x 区间发生变化的时候肯定是把边界的踢出去,此时我们需要更改 t t ,
容易发现它只会修改一个后缀,那么我们用线段树就可以 O ( log n ) O(\log n) 修改.(本质上是一个单调序列取 m a x , s u m max,sum ).

#include<bits/stdc++.h>
#define lc (x<<1)
#define rc (x<<1|1)
using namespace std;
typedef long long ll;
const int N=2055,mod=(int) 1e9+7;

void qr(int &x) {scanf("%d",&x);}

int n,m,L,last[N],col[N],ys[N],tot,vis[N];

struct P {int x,y,c,i; } a[N];
bool cmpx(P a,P b) {return a.x<b.x;}
bool cmpy(P a,P b) {return a.y<b.y; }

struct Q {
    int l[N],r[N];
    void del(int x) {
        l[r[x]]=l[x];
        r[l[x]]=r[x];
    }
} q,nq;

struct node {
    int mn,mx,s,tag;
    node(int a=0,int b=0,int c=0,int d=0) {mn=a; mx=b; s=c; tag=d; }
    node operator+(node b) const {
        return node(min(mn,b.mn),max(mx,b.mx),(s+b.s)%mod,0);
    }
} tr[N*2]; int f[N];

void upd(int x,int l,int r,ll t) {tr[x]=node(t,t,1LL*(ys[r]-ys[l-1])*t%mod,t); }

void bt(int x,int l,int r) {
    if(l==r) {upd(x,l,r,f[l]); return ;}
    int mid=(l+r)/2; bt(lc,l,mid); bt(rc,mid+1,r); tr[x]=tr[lc]+tr[rc];
}

void change(int x,int l,int r,int L,ll t) {
    if(tr[x].mn>=t) return;
    if(tr[x].mx<=t&&l==L) {upd(x,l,r,t); return ;}
    int mid=(l+r)/2;
    if(tr[x].tag) {
        upd(lc,l  ,mid,tr[x].tag);
        upd(rc,mid+1,r,tr[x].tag);
        tr[x].tag=0;
    }
    if(L<=mid) change(lc,l,mid,L,t);
    change(rc,mid+1,r,max(mid+1,L),t);
    tr[x]=tr[lc]+tr[rc];
}

int main() {
    qr(n);; qr(m); qr(L);
    for(int i=1;i<=n;i++)qr(a[i].x),qr(a[i].y),qr(a[i].c);
    sort(a+1,a+n+1,cmpy); a[0].x=a[0].y=-1; a[n+1].x=a[n+1].y=L;
    for(int i=0;i<=n+1;i++) ys[i]=a[i].y,a[i].i=i;
    for(int i=1;i<=n;i++) {
        int l=nq.l[i]=last[a[i].c]; last[a[i].c]=i;
        nq.r[i]=n+1; nq.r[l]=i;
    }
    sort(a+1,a+n+1,cmpx); ll ans=0;
    for(int i=1;i<=n;i++) {
        if(a[i].x^a[i-1].x) {
            for(int j=1;j<i;j++) col[a[j].i]=0;
            for(int j=i;j<=n;j++) col[a[j].i]=a[j].c;
            for(int l=1,r=1;l<=n;l++) {
                for( ;r<=n&&tot<m;r++) if(col[r]&&!vis[col[r]]++) tot++;
                f[l]=(tot<m)?L:ys[r-1];if(col[l]&&!--vis[col[l]]) tot--;
            }
            q=nq; bt(1,1,n);
            for(int j=n;j>=i;j--) {
                ans += (1LL*(ys[n]+1)*L-tr[1].s)%mod*(a[i].x-a[i-1].x)%mod*(a[j+1].x-a[j].x)%mod;
                int id=a[j].i,l=q.l[id],r=q.r[id];
                change(1,1,n,l+1,ys[r]); q.del(id);
            }
            ans%=mod;
        }
        nq.del(a[i].i);
    }
    printf("%lld\n",ans); return 0;
}

猜你喜欢

转载自blog.csdn.net/qq_42886072/article/details/108344717