[COCI2017-2018#1] Plahte

题面很长,可往往真正有用的题意却没有这么长,例如说这么一句:

  • 床单放在上面,使它们之间角或边不会互相接触, 边也不会相交,但他可能把较小的床单放在大的上面,或者一个完全覆盖另个。

从这句话中,我们可以看出,矩形是不会相交的,且只有包含关系。所以,我们仅需记录一个矩形的父亲为包含它的所有矩形中最小的那个,原图可由此化为一棵森林。

那么,我们怎么构造一个森林呢?我们这里有两种方法:

  • 用扫描线,线段树处理y轴,每次找到一个矩形之后判断这个矩形的下边界是否合法,如不合法就倍增往其父亲走,直到合法。
  • 对于y轴,我们建一棵标记永久化的线段树。扫到左边界时,直接把标记打到相应区间,直接覆盖。

构造出森林过后,我们就可以在此森林上进行线段树合并或者是set启发式合并即可。

 

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<set>
#include<queue>
#include<ctime>
#define MAXN 200005
#define ll long long
#define maxn 15
#define maxs 1000005
#define inf 1e9
#define eps 1e-9
using namespace std;
inline char gc() {
    static char now[1<<16],*S,*T;
    if (T==S) {
        T=(S=now)+fread(now,1,1<<16,stdin);
        if (T==S) return EOF;
    }
    return *S++;
}
inline ll readlong() {
    ll x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x*=10;
        x+=ch-'0';
        ch=getchar();
    }
    return x*f;
}
inline int read() {
    int x=0,f=1;
    char ch=getchar();
    while(ch<'0'||ch>'9') {
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9') {
        x*=10;
        x+=ch-'0';
        ch=getchar();
    }
    return x*f;
}
void putint(long long t) {
    int ans[40]= {0};
    for(; t; t/=10)ans[++ans[0]]=t%10;
    for(; ans[0]; ans[0]--)putchar('0'+ans[ans[0]]);
    putchar('\n');
}
const int N=500005;
int n,m;
struct edge{
    int to,nxt;
}e[N];
int h[N],cnt;
void add(int x,int y){
//  cout<<x<<' '<<y;
    e[++cnt]=(edge){y,h[x]};
    h[x]=cnt;
}
int pre[N],_k[N],_y[N],rt[N];
int ans[N];
int dx,dy,dv;
int g[N<<5];
int col[N<<5],f[N<<5][2];
int cntt,cnty,cnts;
int tot;
struct bed{
    int x,y,k,i;
    bool operator <(const bed &a)const{
        if(x==a.x){
            return i>a.i;
        }
        return x<a.x;
    }
}mp[N<<2];
int query(int x,int l,int r){
    if(g[x]>=0||l==r){
        return g[x];
    }
    if(!x){
        return 0;
    }
    int mid=(l+r)>>1;
    if(dx<=mid){
        return query(x<<1,l,mid);
    }
    else{
        return query(x<<1|1,mid+1,r);
    }
}
void push(int x){
    if(g[x]<0){
        return ;
    }
    g[x<<1]=g[x<<1|1]=g[x];
    g[x]=-1;
}
void modify(int x,int l,int r){
//  cout<<"Asdf"<<endl;
    if(dx<=l&&dy>=r){
        g[x]=dv;
        return ;
    }
    push(x);
    int mid=(l+r)>>1;
    if(dx<=mid){
        modify(x<<1,l,mid);
    }
    if(dy>mid){
        modify(x<<1|1,mid+1,r);
    }
}
void add_cor(int &x,int l,int r){
    if(!x){
        x=++tot;
    }
    if(l==r){
        col[x]=1;
        return;
    }
    int mid=(l+r)>>1;
    if(dx<=mid){
        add_cor(f[x][0],l,mid);
    }
    else{
        add_cor(f[x][1],mid+1,r);
    }
    col[x]=col[f[x][0]]+col[f[x][1]];
}
int merge(int a,int b,int l,int r){
    if(!a|!b){
        return a^b;
    }
    if(l==r){
        return a;
    }
    int mid=(l+r)>>1;
    f[a][0]=merge(f[a][0],f[b][0],l,mid);
    f[a][1]=merge(f[a][1],f[b][1],mid+1,r);
    col[a]=col[f[a][0]]+col[f[a][1]];
    return a;
}
void dfs(int x){
    for(int i=h[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y!=pre[x]){
            dfs(y);
            rt[x]=merge(rt[x],rt[y],1,cnts);
        }
    }
    ans[x]=col[rt[x]];
}
int main(){
    memset(g,-1,sizeof(g));
    n=read();
    m=read();
    for(int i=1;i<=n;i++){
        int a=read();
        int b=read();
        int c=read();
        int d=read();
        mp[++cntt]=(bed){a,b,d,i};
        _y[++cnty]=b;
        mp[++cntt]=(bed){c,b,d,-i};
        _y[++cnty]=d;
    }
    for(int i=1;i<=m;i++){
        int x=read();
        int y=read();
        int k=read();
        mp[++cntt]=(bed){x,y,k,0};
        _y[++cnty]=y;
        _k[++cnts]=k;
    }
    sort(_y+1,_y+1+cnty);
    cnty=unique(_y+1,_y+1+cnty)-_y-1;
    sort(_k+1,_k+1+cnts);
    cnts=unique(_k+1,_k+1+cnts)-_k-1;
    sort(mp+1,mp+cntt+1);
    for(int i=1;i<=cntt;i++){
        mp[i].y=lower_bound(_y+1,_y+1+cnty,mp[i].y)-_y;
        if(mp[i].i){
            mp[i].k=lower_bound(_y+1,_y+1+cnty,mp[i].k)-_y;
            if(mp[i].i>0){
                dx=mp[i].y;
                dy=mp[i].k;
                dv=mp[i].i;
                if((pre[dv]=query(1,1,cnty))>0){
                    add(pre[dv],dv);
                }
                modify(1,1,cnty);
                continue;
            }
            dx=mp[i].y;
            dy=mp[i].k;
            dv=max(pre[-mp[i].i],0);
            modify(1,1,cnty);
            continue;
        }
        mp[i].k=lower_bound(_k+1,_k+cnts+1,mp[i].k)-_k;
        dx=mp[i].y;
        int tmp=query(1,1,cnty);
        if(tmp>0){
            dx=mp[i].k;
            add_cor(rt[tmp],1,cnts);
        }
    }
    for(int i=1;i<=n;i++){
        if(pre[i]<=0){
            dfs(i);
        }
    }
    for(int i=1;i<=n;i++){
        printf("%d\n",ans[i]);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/LHbz/p/9720950.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值