NOIP模拟(20171031)T3 纸带

本文探讨了一种区间染色问题的解决方法,通过线段树和并查集两种不同的数据结构实现,旨在寻找最终纸带上颜色种类的数量。文章首先介绍了线段树的实现细节,并指出了离散化过程中可能遇到的问题及解决方案,随后提出使用并查集进行优化的方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

有一个纸带·,每次区间染色,同一个地方后染的颜色覆盖先染的,求最后纸带上颜色种类数。
咦,这不是线段树吗?
咦,倒过来做好像很方便啊?
咦,离散化有坑?
区间涂色,若该颜色所在区间均被染色,则该颜色按原顺序操作后会被后面的颜色覆盖
注意离散化
本来三个颜色3-8,2-5,7-9,最后6号点上颜色是1
离散化后为2-5,1-3,4-6,颜色1就被“吃”掉了
怎么解决?自(kan)行(dai)思(ma)考(ba)

#include<bits/stdc++.h>
#define LEN 1000005
using namespace std;
inline int getint(){
    int x=0,p=1;
    char c=getchar();
    while(!isdigit(c)){
        if(c=='-')p=-1;
        c=getchar();
    }
    while(isdigit(c)){
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    return x*p;
}
inline void putint(int x){
    if(x<0){
        putchar('-');
        x=-x;
    }
    static int buf[30];
    int tot=0;
    do{
        buf[tot++]=x%10;
        x/=10;
    }while(x);
    while(tot)putchar(buf[--tot]+'0');
}
int a[LEN],b[LEN],x[LEN<<3];
bool seg[(LEN<<4)+1],tag[(LEN<<4)+1];
inline void updata(int k){
    seg[k]=seg[k<<1]&&seg[k<<1|1];
}
inline void change(int k){
    seg[k]=tag[k]=1;
}
inline void pushdown(int k){
    if(tag[k]){
        change(k<<1);
        change(k<<1|1);
        tag[k]=0;
    }
}
void modify(int k,int l,int r,int s,int t){
    if(seg[k])return;
    pushdown(k);
    if(s<=l&&r<=t){
        change(k);
        return;
    }
    int mid=l+r>>1;
    if(s<=mid)modify(k<<1,l,mid,s,t);
    if(mid<t)modify(k<<1|1,mid+1,r,s,t);
    updata(k);
}
bool query(int k,int l,int r,int s,int t){
    if(seg[k])return true;
    pushdown(k);
    if(s<=l&&r<=t){
        return seg[k];
    }
    int mid=l+r>>1;
    bool ans=1;
    if(s<=mid)ans=ans&&query(k<<1,l,mid,s,t);
    if(mid<t)ans=ans&&query(k<<1|1,mid+1,r,s,t);
    updata(k);
    return ans;
}
int main(){
    int n=getint();
    for(int i=1;i<=n;++i){
        a[i]=getint()+1,b[i]=getint();
        x[i*2-1]=a[i],x[i*2]=b[i];
    }
    int tot=n*2;
    for(int i=1;i<=n;++i){
        x[++tot]=b[i]+1;
    }
    sort(x+1,x+tot+1);
    tot=unique(x+1,x+tot+1)-x;
    for(int i=1;i<=n;++i){
        a[i]=lower_bound(x+1,x+tot,a[i])-x;
        b[i]=lower_bound(x+1,x+tot,b[i])-x;
    }
    int ans=0;
    for(int i=n;i>=1;--i){
        if(!query(1,1,tot,a[i],b[i])){
            ++ans;
            modify(1,1,tot,a[i],b[i]);
        }
    }
    cout<<ans<<endl;
    return 0;
}

咦,正解竟然是并查集!
我们将区间覆盖用并查集优化,维护当前颜色最右端,合并时将一段颜色的最右端与相邻格子合并即可

#include<bits/stdc++.h>
#define LEN 2000006
using namespace std;
inline int getint(){
    int x=0,p=1;
    char c=getchar();
    while(!isdigit(c)){
        if(c=='-')p=-1;
        c=getchar();
    }
    while(isdigit(c)){
        x=(x<<3)+(x<<1)+(c^'0');
        c=getchar();
    }
    return x*p;
}
int ans=0;
int a[LEN],b[LEN],x[LEN];
int fa[LEN];
inline int getfa(int x){
    return fa[x]=(fa[x]==x?x:getfa(fa[x]));
}
inline int merge(int u,int v){
    u=getfa(u),v=getfa(v);
    return fa[u]=v;
}
inline void modify(int l,int r){
    if(getfa(l)>=r)return;
    ++ans;
    for(int i=getfa(l);i<r;i=merge(i,i+1));
}
int main(){
    int n=getint();
    for(int i=1;i<=n;++i){
        a[i]=getint(),b[i]=getint();
        x[i*2-1]=a[i],x[i*2]=b[i];
    }
    int tot=n*2;
    sort(x+1,x+tot+1);
    tot=unique(x+1,x+tot+1)-x;
    for(int i=1;i<=n;++i){
        a[i]=lower_bound(x+1,x+tot,a[i])-x;
        b[i]=lower_bound(x+1,x+tot,b[i])-x;
    }
    for(int i=1;i<=tot;++i){
        fa[i]=i;
    }
    for(int i=n;i>=1;--i){
        modify(a[i],b[i]);
    }
    cout<<ans<<endl;
    return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值