题解 [PR #6] 区间数颜色
围观自己被吊打.jpg
- 关于动态开点线段树的区间染色:在
upd
时注意if (区间染完了) return;
,否则会在对这个区间做pushup
时出问题 - 在
c++20
中所有基于struct
的比较函数都必须加const
,否则会 CE
先说特殊性质:
只有相交和不包含可以建出树来转化为链加
没有包含只会一个基于颜色段均摊(应该是这个名字吧)的做法
首先所有左端点互不相等,按左端点排序后 \(r_{i-1}<r_i\)
那么删一个区间后后面的区间左端点与被删区间右端点+1 取 \(\max\)
发现每次是把一个区间染为同种颜色,所以 set 维护同色段,每个同色段在线段树上区间修改
将所有 \(<0\) 的位置拎出来放到一个堆里单独维护
然后还要把前面的区间右端点与被删区间左端点-1 取 \(\min\) 用同样方法维护即可
然后正解:
发现若 \(i\) 包含 \(j\),则 \(i\) 一定在 \(j\) 之后被删除,所以可以在删除 \(j\) 之后再加入候选集合
那么就转化为了没有包含关系的情况
然后这种情况也有更简单的做法
离散化后值域区间被分为 \(O(n)\) 个段
区间按左端点排序后包含一个段的区间也是连续的一段
那么可以二分出来区间修改
用 set 维护未被删除的值域段即可
然后一个区间被选定删除之后要加入包含它且不再包含其它区间的区间
找到被删除区间在候选集合中的前驱后继
发现可能被加入的区间编号上在前驱之后,且 \(r\) 小于后继的 \(r\)
那么也可以用线段树找到
复杂度 \(O(n\log n)\),细节极多
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 500010
#define ll long long
//#define int long long
char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n;
bool del[N];
int uni[N], sta[N], bit[N], pos[N], usiz, top;
struct range{int l, r, id;}a[N];
set<int> rest;
struct cmp1{inline bool operator () (range a, range b) const {return a.l==b.l?a.id<b.id:a.l<b.l;}};
set<range, cmp1> s1;
struct cmp2{inline bool operator () (range a, range b) const {return a.r==b.r?a.id<b.id:a.r<b.r;}};
set<range, cmp2> s2;
struct seg{ll minn; int mini;};
inline seg operator + (seg a, seg b) {
int minn=min(a.minn, b.minn), mini=0;
if (a.minn==minn) mini=max(mini, a.mini);
if (b.minn==minn) mini=max(mini, b.mini);
return {minn, mini};
}
inline void upd(int i, int dat) {for (; i<=usiz; i+=i&-i) bit[i]+=dat;}
inline int query(int l, int r) {
int ans=0; --l;
while (r>l) ans+=bit[r], r-=r&-r;
while (l>r) ans-=bit[l], l-=l&-l;
return ans;
}
namespace seg1{
#define tl(p) tl[p]
#define tr(p) tr[p]
int tl[N<<2], tr[N<<2];
ll minn[N<<2], mini[N<<2], tag[N<<2];
inline void spread(int p) {
if (!tag[p]) return ;
minn[p<<1]+=tag[p]; tag[p<<1]+=tag[p];
minn[p<<1|1]+=tag[p]; tag[p<<1|1]+=tag[p];
tag[p]=0;
}
inline void pushup(int p) {
minn[p]=min(minn[p<<1], minn[p<<1|1]);
mini[p]=INF;
if (minn[p]==minn[p<<1]) mini[p]=min(mini[p], mini[p<<1]);
if (minn[p]==minn[p<<1|1]) mini[p]=min(mini[p], mini[p<<1|1]);
}
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r;
if (l==r) {
if (del[l]) minn[p]=INF;
else minn[p]=uni[a[l].r]-uni[a[l].l];
mini[p]=a[l].id;
return ;
}
int mid=(l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
pushup(p);
}
void upd(int p, int l, int r, ll dat) {
if (l<=tl(p)&&r>=tr(p)) {minn[p]+=dat; tag[p]+=dat; return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (l<=mid) upd(p<<1, l, r, dat);
if (r>mid) upd(p<<1|1, l, r, dat);
pushup(p);
}
void upd(int p, int pos, ll dat) {
if (tl(p)==tr(p)) {minn[p]=dat; return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (pos<=mid) upd(p<<1, pos, dat);
else upd(p<<1|1, pos, dat);
pushup(p);
}
}
namespace seg2{
#define tl(p) tl[p]
#define tr(p) tr[p]
#define pushup(p) dat[p]=dat[p<<1]+dat[p<<1|1]
int tl[N<<2], tr[N<<2]; seg dat[N<<2];
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r;
if (l==r) {dat[p]={!del[l]?INF:a[l].r, l}; return ;}
int mid=(l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
pushup(p);
}
void upd(int p, int pos, ll val) {
if (tl(p)==tr(p)) {dat[p].minn=val; return ;}
int mid=(tl(p)+tr(p))>>1;
if (pos<=mid) upd(p<<1, pos, val);
else upd(p<<1|1, pos, val);
pushup(p);
}
seg query(int p, int l, int r) {
if (l<=tl(p)&&r>=tr(p)) {return dat[p];}
int mid=(tl(p)+tr(p))>>1;
if (l<=mid&&r>mid) return query(p<<1, l, r)+query(p<<1|1, l, r);
else if (l<=mid) return query(p<<1, l, r);
else return query(p<<1|1, l, r);
}
}
signed main()
{
n=read();
for (int i=1; i<=n; ++i) {
uni[++usiz]=(a[i].l=read());
uni[++usiz]=(a[i].r=read());
a[i].id=i;
}
sort(uni+1, uni+usiz+1);
usiz=unique(uni+1, uni+usiz+1)-uni-1;
for (int i=1; i<=n; ++i) {
a[i].l=lower_bound(uni+1, uni+usiz+1, a[i].l)-uni;
a[i].r=lower_bound(uni+1, uni+usiz+1, a[i].r)-uni;
}
sort(a+1, a+n+1, [](range a, range b){return a.l==b.l?a.r>b.r:a.l<b.l;});
// cout<<"a: "; for (int i=1; i<=n; ++i) cout<<"("<<a[i].l<<','<<a[i].r<<") "; cout<<endl;
for (int i=1; i<=n; ++i) pos[a[i].id]=i;
for (int i=n,rmin=INT_MAX; i; --i) {
if (rmin<=a[i].r) del[i]=1;
else s1.insert(a[i]), s2.insert(a[i]);
rmin=min(rmin, a[i].r);
}
// cout<<"del: "; for (int i=1; i<=n; ++i) cout<<del[i]<<' '; cout<<endl;
for (int i=1; i<usiz; ++i) upd(i, uni[i+1]-uni[i]), rest.insert(i);
seg1::build(1, 1, n); seg2::build(1, 1, n);
for (int i=1; i<=n; ++i) {
// cout<<"i: "<<i<<endl;
int tem=seg1::mini[1];
printf("%d%c", tem, " \n"[i==n]);
// cout<<"used: "<<pos[tem]<<endl;
seg1::upd(1, pos[tem], pos[tem], INF);
for (auto it=rest.lower_bound(a[pos[tem]].l); it!=rest.end()&&*it<a[pos[tem]].r; it=rest.erase(it)) {
// cout<<"cover: "<<*it<<endl;
// cout<<"s1: "; for (auto it:s1) cout<<"("<<it.l<<','<<it.r<<") "; cout<<endl;
// cout<<"s2: "; for (auto it:s2) cout<<"("<<it.l<<','<<it.r<<") "; cout<<endl;
int l=pos[s2.lower_bound({0, *it, INT_MAX})->id];
int r=pos[(--s1.upper_bound({*it, 0, INT_MAX}))->id];
// cout<<"lr: "<<l<<' '<<r<<endl;
seg1::upd(1, l, r, uni[*it]-uni[*it+1]);
upd(*it, uni[*it]-uni[*it+1]);
}
while (1) {
auto it=s1.lower_bound(a[pos[tem]]);
int pre=it==s1.begin()?1:pos[(--it)->id];
// cout<<"pre: "<<pre<<endl;
it=s1.lower_bound(a[pos[tem]]);
int lim=(++it)==s1.end()?INF:it->r;
// cout<<"lim: "<<lim<<endl;
int suf=seg2::query(1, pre, n).mini;
// cout<<"suf: "<<suf<<endl;
if (del[suf] && a[suf].r<lim) {
// cout<<"add: "<<suf<<endl;
del[suf]=0;
// cout<<"val: "<<query(a[suf].l, a[suf].r-1)<<endl;
seg1::upd(1, suf, query(a[suf].l, a[suf].r-1));
seg2::upd(1, suf, INF);
s1.insert(a[suf]); s2.insert(a[suf]);
lim=min(lim, a[suf].r);
}
else break;
}
s1.erase(a[pos[tem]]); s2.erase(a[pos[tem]]);
}
return 0;
}