zoukankan      html  css  js  c++  java
  • 题解-CF1389F Bicolored Segments

    题面

    CF1389F Bicolored Segments

    (n) 条线段 ([l_i,r_i]),每条有个颜色 (t_iin{0,1}),求最多选出多少条线段,使没有不同颜色的线段相交。

    数据范围:(1le nle 2cdot 10^5)(1le l_ile r_ile 10^9)


    蒟蒻语

    昨天蒟蒻打 CF,发挥得不错,迷惑回橙。但是蒟蒻没做出这题,赛后想了好久感觉这题很奇妙,于是蒻蒻地来写篇题解。


    蒟蒻解一

    线段树维护 dp。

    先将每条线段 (l_i,r_i) 离散化,坐标范围为 ([0,cnt])

    (f(i,j,k)) 表示看了 ([0,i])([j+1,i]) 的线段颜色都为 (k) 的最多线段数。

    [j<i:f(i,j,k)=f(i-1,j,k)+sum_{x=1}^{n}[l_x>j][r_x=i] ]

    [f(i,i,k)=max[max_{j=0}^{i-1}f(i,j,!k),max_{j=0}^{i-1}f(i,j,k)] ]

    那么答案是 (max_{j=0}^{cnt}f(cnt,j,0/1))

    (ca_i) 这个 vector 存放 (r_x=i)(x)

    所以可以用一个线段树代替 (j) 维,把 (i) 维滚掉,实现上述dp。

    时间复杂度 (Theta(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
     
    //Start
    typedef long long ll;
    typedef double db;
    #define mp(a,b) make_pair(a,b)
    #define x first
    #define y second
    #define be(a) a.begin()
    #define en(a) a.end()
    #define sz(a) int((a).size())
    #define pb(a) push_back(a)
    const int inf=0x3f3f3f3f;
    const ll INF=0x3f3f3f3f3f3f3f3f;
     
    //Data
    const int N=2e5,M=(N<<1)+1;
    int n,l[N],r[N],t[N],cnt,b[M],ans;
    vector<int> ca[M];
     
    //Segmenttree
    const int T=M<<2;
    #define lk k<<1
    #define rk k<<1|1
    struct Segmenttree{ //线段树,下标为坐标,维护区间加、全局最大值
        int mx[T],mk[T];
        void pushup(int k){mx[k]=max(mx[lk],mx[rk]);}
        void pm(int k,int v){mk[k]+=v,mx[k]+=v;}
        void pushdown(int k){if(mk[k]) pm(lk,mk[k]),pm(rk,mk[k]),mk[k]=0;}
        void fix(int x,int y,int v,int k,int l,int r){
            if(x<=l&&r<=y) return pm(k,v);
            pushdown(k);
            int mid=(l+r)>>1;
            if(mid>=x) fix(x,y,v,lk,l,mid);
            if(mid<y) fix(x,y,v,rk,mid+1,r);
            pushup(k);
        }
        int Mx(){return mx[1];}
        void Print(int k,int l,int r){
            if(l==r){cout<<mx[k]<<' ';return;}
            pushdown(k);
            int mid=(l+r)>>1;
            Print(lk,l,mid),Print(rk,mid+1,r);
        }
    }g[2];
     
    //Main
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
        cin>>n;
        for(int i=0;i<n;i++){
            cin>>l[i]>>r[i]>>t[i],--t[i];
            b[cnt++]=l[i],b[cnt++]=r[i];
        }
        b[cnt++]=0,sort(b,b+cnt),cnt=unique(b,b+cnt)-b;
        for(int i=0;i<n;i++){
            l[i]=lower_bound(b,b+cnt,l[i])-b;
            r[i]=lower_bound(b,b+cnt,r[i])-b;
            ca[r[i]].pb(i);
        }
        for(int i=1;i<cnt;i++){
            for(int x:ca[i]) g[t[x]].fix(0,l[x]-1,1,1,0,cnt);
            g[0].fix(i,i,g[1].Mx(),1,0,cnt),g[1].fix(i,i,g[0].Mx(),1,0,cnt);//这么写也是可以的
        }
        cout<<max(g[0].Mx(),g[1].Mx())<<'
    ';
        return 0;
    }
    

    蒟蒻解二

    萌新初学 OI 的时候,有一个贪心问题:求最多线段互不相交。做法是右端点再左端点双关键字排序,然后贪心取舍一下。

    这题可以同样地骚操作:

    初始化答案为 (n)。用两个 multiset 记录两种颜色分别选了哪些线段。

    顺序枚举排序了的线段,如果没有选了的线段与当前线段异色并重合,那么蒟蒻们可以很开心地选上这条线段。

    否则把右端点在当前线段左端点右边并且最近的异色线段从 multiset 中删除,不往 multiset 中加入当前线段,把答案 (-1),表示一个对抗抵消的过程。

    比如加了一条 (0) 线段,然后再加一条 (1) 线段与它抵消。这时如果来 (2)(1) 线段,相当于选了 (3)(1) 线段;如果来 (2)(0) 线段,相当于选了 (3)(0) 线段。

    这种思想类似求序列众数时的对抗抵消选举和模拟网络流反悔推流。

    时间复杂度 (Theta(nlog n))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    //Start
    typedef long long ll;
    typedef double db;
    #define mp(a,b) make_pair(a,b)
    #define x first
    #define y second
    #define be(a) a.begin()
    #define en(a) a.end()
    #define sz(a) int((a).size())
    #define pb(a) push_back(a)
    const int inf=0x3f3f3f3f;
    const ll INF=0x3f3f3f3f3f3f3f3f;
    
    //Data
    const int N=2e5;
    int n,ans;
    struct S{int l,r,t;}a[N];
    multiset<int> g[2];
    
    //Main
    int main(){
        ios::sync_with_stdio(0);
        cin.tie(0),cout.tie(0);
        cin>>n,ans=n;
        for(int i=0;i<n;i++)
            cin>>a[i].l>>a[i].r>>a[i].t,--a[i].t;
        sort(a,a+n,[&](const S p,const S q){return p.r==q.r?p.l<q.l:p.r<q.r;});
        for(int i=0;i<n;i++)
            if(g[!a[i].t].lower_bound(a[i].l)==en(g[!a[i].t])) g[a[i].t].insert(a[i].r);
            else ans--,g[!a[i].t].erase(g[!a[i].t].lower_bound(a[i].l));
        cout<<ans<<'
    ';
        return 0;
    }
    

    祝大家学习愉快!

  • 相关阅读:
    产品经理的未来在哪里
    如何打造一款成功的产品
    彻底解决Android 应用方法数不能超过65K的问题
    MVC,MVP 和 MVVM 的图示
    oracle最高账号sys的密码认证模式
    Android开发者必须深入学习的10个应用开源项目
    Android程序完全退出的三种方法
    Android-监听网络状态
    Android开发图片分辨率问题解决方案
    Android清除本地数据缓存代码
  • 原文地址:https://www.cnblogs.com/George1123/p/13406024.html
Copyright © 2011-2022 走看看