题面

CF1389F Bicolored Segments

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

数据范围:\(1\le n\le 2\cdot 10^5\),\(1\le l_i\le r_i\le 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(n\log 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())<<'\n';
return 0;
}

蒟蒻解二

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

这题可以同样地骚操作:

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

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

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

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

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

时间复杂度 \(\Theta(n\log 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<<'\n';
return 0;
}

祝大家学习愉快!

题解-CF1389F Bicolored Segments的更多相关文章

  1. Codeforces Educational Round 92 赛后解题报告(A-G)

    Codeforces Educational Round 92 赛后解题报告 惨 huayucaiji 惨 A. LCM Problem 赛前:A题嘛,总归简单的咯 赛后:A题这种**题居然想了20m ...

  2. CodeForces 430A Points and Segments (easy)(构造)题解

    题意:之前愣是没看懂题意...就是给你n个点的坐标xi,然后还规定了Li,Ri,要求给每个点染色,每一组L,R内的点红色和黑色的个数不能相差大于1个,问你能不能染成功,不能输出-1,能就按照输入的顺序 ...

  3. LeetCode题解之Number of Segments in a String

    1.题目描述 2.题目分析 找到字符串中的空格即可 3.代码 int countSegments(string s) { ){ ; } vector<string> v; ; i < ...

  4. PAT甲题题解-1104. Sum of Number Segments (20)-(水题)

    #include <iostream> #include <cstdio> #include <algorithm> #include <string.h&g ...

  5. POJ3304:Segments——题解

    http://poj.org/problem?id=3304 题目大意:给n条线段,求是否存在一条直线,将所有线段投影到上面,使得所有投影至少交于一点. ——————————————————————— ...

  6. [CF1167D]Bicolored RBS题解

    模拟两个颜色的扩号层数,贪心,如果是左括号,哪边的层数浅就放那边:如果是右括号,哪边的层数深就放那边. 至于层数的维护,两个int就做掉了 放个代码: #include <cstdio> ...

  7. [CF846C]Four Segments题解

    我们暴力枚举一下\(delim_{1}\) 然后对于每个\(delim_{1}\),O(n)扫一遍+前缀和求出最大\(delim_{0}\)和\(delim_{2}\),然后记录一下它们的位置就行啦 ...

  8. 【题解】CF1426D Non-zero Segments

    题目戳我 \(\text{Solution:}\) 若\([l,r]\)子段和是\(0,\)则\(sum[r]=sum[l-1].\) 于是我们可以考虑维护当前哪一个前缀和出现过.对于区间\([l,r ...

  9. Codeforces Round #337 (Div. 2) D. Vika and Segments 线段树 矩阵面积并

    D. Vika and Segments     Vika has an infinite sheet of squared paper. Initially all squares are whit ...

随机推荐

  1. centos6 安装 terminator

    yum install terminator 报错: No package terminator available. 解决: yum install epel-release 报错 Cannot r ...

  2. JLC PCB 嘉立创自动确认生产稿,不讲武德?耗子尾汁!!!

    首先,开局一张图,嘉立创又不做人的一天.嘉立创不讲武德,耗子尾汁!!! 之前下单,勾选了确定生产稿和不加客编,结果生产稿出来还是给我加了客编.那我出10元的意思何在?让我自己花3元看我花的10元有没有 ...

  3. [LeetCode题解]142. 环形链表 II | 快慢指针

    解题思路 本题是在141. 环形链表基础上的拓展,如果存在环,要找出环的入口. 如何判断是否存在环,我们知道通过快慢指针,如果相遇就表示有环.那么如何找到入口呢? 如下图所示的链表: 当 fast 与 ...

  4. 纯干货分享!2020阿里java岗笔试面试题总结(附答案)

    前言 2020金九银十马上结束,现为大家整理了这次金九银十面试阿里的面试题总结,都是我从朋友那拿到的面试真题,话不多说,满满的干货分享给大家! int a=10是原子操作吗? 是的.  注意点: i+ ...

  5. 如何使用iMazing编辑iOS设备的备份

    乍一看,编辑iPhone或iPad的备份似乎是一个奇怪的命题,但实际上这样做的原因有很多,例如在备份数据损坏时进行修复,又如合并来自不同设备的数据. iMazing对备份文件编辑的支持非常全面,即使备 ...

  6. CodeChef-LECOINS Little Elephant and Colored Coins 题解

    CodeChef-LECOINS Little Elephant and Colored Coins Little Elephant and Colored Coins The Little Elep ...

  7. C语言讲义——预处理

    C预处理器是一个文本替换工具,在实际编译之前完成一些预先的处理. C预处理器(C Preprocessor)简写为 CPP 预处理器命令都是以#开头,如: #include <stdio.h&g ...

  8. 编曲技巧:使用FL Studio来制作停顿的效果

    停顿效果是一种在音乐创作中非常常用的音效,它能起到缓冲的作用,而且能使这段旋律更具节奏感,在比较激情的歌曲中尤为常见.例如知名歌手王力宏演唱的<火力全开>中就使用了停顿效果,为歌曲加了不少 ...

  9. (在模仿中精进数据可视化05)疫情期间市值增长top25公司

    本文完整代码及数据已上传至我的Github仓库https://github.com/CNFeffery/FefferyViz 1 简介 新冠疫情对很多实体经济带来冲击的同时,也给很多公司带来了新的增长 ...

  10. C++语言中std::array的神奇用法总结,你需要知道!

    摘要:在这篇文章里,将从各个角度介绍下std::array的用法,希望能带来一些启发. td::array是在C++11标准中增加的STL容器,它的设计目的是提供与原生数组类似的功能与性能.也正因此, ...