题目
n(n<=2e5)个区间,第i个区间[li,ri](1<=li<=ri<=1e9),
第i个区间要么是被颜色1染色,要么是被颜色2染色,
现在要选一些区间,选中的区间中, 不同颜色的区间不能相交,问最多能选多少个区间
思路来源
zlc大佬:“dp,从左往右看答案一定是1111222211112222这样交叉着一坨一坨的对吧,需要满足相邻的1那坨和2那坨没有交叉”
所以,线段树就是维护前面那一坨的右端点r,与后面那一坨的左端点l分开即可
题解
线段树dp[i].x维护,n个区间中,颜色i的区间的末位置在x时(此时仅考虑右端点<=i的区间),选中的区间个数最大值
考虑颜色1最后一个区间的右端点x,x+1之后可以接若干个颜色2的区间,彼此相交无所谓,
将相同右端点的区间同时考虑,
[l,r]颜色2,会令右端点[1,l-1]的颜色1的dp值+1,
因为接在了另一个颜色的后面,颜色内部不互相影响
而枚举最后一段颜色,此时可以将最后一段颜色以r结尾,
故用某个以r为右端点的区间更新对应颜色的r位置的dp值
最终的答案,就是两棵线段树中,全局的最大值
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=4e5+10;
int n,a[N],c;
struct segtree{
int n;
struct node{int l,r,v,c;}e[N<<2];
#define l(p) e[p].l
#define r(p) e[p].r
#define v(p) e[p].v
#define c(p) e[p].c
void up(int p){v(p)=max(v(p<<1),v(p<<1|1));}
void bld(int p,int l,int r){
l(p)=l;r(p)=r;
c(p)=0;
if(l==r){v(p)=0;return;}
int mid=l+r>>1;
bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
up(p);
}
void init(int _n){n=_n;bld(1,0,n);}
void psd(int p){
v(p<<1)+=c(p);v(p<<1|1)+=c(p);
c(p<<1)+=c(p);c(p<<1|1)+=c(p);
c(p)=0;
}
void chg(int p,int x,int w){
if(l(p)==r(p)){v(p)=max(v(p),w);return;}
int mid=l(p)+r(p)>>1;
chg(p<<1|(x>mid),x,w);
up(p);
}
void add(int p,int ql,int qr,int w){
if(ql<=l(p)&&r(p)<=qr){
v(p)+=w;
c(p)+=w;
return;
}
int mid=l(p)+r(p)>>1;
psd(p);
if(ql<=mid)add(p<<1,ql,qr,w);
if(qr>mid)add(p<<1|1,ql,qr,w);
up(p);
}
int ask(int p,int ql,int qr){
if(ql<=l(p)&&r(p)<=qr)return v(p);
int mid=l(p)+r(p)>>1,res=0;
psd(p);
if(ql<=mid)res=max(res,ask(p<<1,ql,qr));
if(qr>mid)res=max(res,ask(p<<1|1,ql,qr));
return res;
}
}dp[2];
struct node{
ll l,r,op;
}e[N];
bool operator<(node a,node b){
return a.r<b.r||(a.r==b.r && a.l>b.l);
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].op);
a[++c]=e[i].l;a[++c]=e[i].r;
e[i].op--;
}
sort(a+1,a+c+1);
c=unique(a+1,a+c+1)-(a+1);
for(int i=0;i<2;++i){
dp[i].init(c);
}
for(int i=1;i<=n;++i){
e[i].l=lower_bound(a+1,a+c+1,e[i].l)-a;
e[i].r=lower_bound(a+1,a+c+1,e[i].r)-a;
}
sort(e+1,e+n+1);
for(int i=1;i<=n;){
int j=i;
while(j+1<=n && e[j+1].r==e[i].r){
j++;
}
for(int k=i;k<=j;++k){
int l=e[k].l,r=e[k].r,op=e[k].op;
dp[op^1].add(1,0,l-1,1);
}
int ans[2]={0,0};
for(int k=i;k<=j;++k){
int l=e[k].l,r=e[k].r,op=e[k].op;
ans[op]=max(ans[op],dp[op^1].ask(1,0,l-1));
}
for(int k=0;k<2;++k){
dp[k].chg(1,e[i].r,ans[k]);
}
i=j+1;
}
printf("%d\n",max(dp[0].ask(1,0,c),dp[1].ask(1,0,c)));
return 0;
}
Bonus
题目
题解
将前2*n行也当成必取的线段,因为每个位置必须要涂一种颜色,不能不涂
而后m行,也当做必取的线段,因为每条bonus线段的权值均为正,
然后相同的套路,线段树加速一下就好了
代码
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=3e5+10;
const ll mn=-1e15;
int n,m;
struct segtree{
int n;
struct node{int l,r;ll v,c;}e[N<<2];
#define l(p) e[p].l
#define r(p) e[p].r
#define v(p) e[p].v
#define c(p) e[p].c
void up(int p){v(p)=max(v(p<<1),v(p<<1|1));}
void bld(int p,int l,int r){
l(p)=l;r(p)=r;
c(p)=0;
if(l==r){v(p)=(l==0?0:mn);return;}
int mid=l+r>>1;
bld(p<<1,l,mid);bld(p<<1|1,mid+1,r);
up(p);
}
void init(int _n){n=_n;bld(1,0,n);}
void psd(int p){
v(p<<1)+=c(p);v(p<<1|1)+=c(p);
c(p<<1)+=c(p);c(p<<1|1)+=c(p);
c(p)=0;
}
void chg(int p,int x,ll w){
if(l(p)==r(p)){v(p)=max(v(p),w);return;}
int mid=l(p)+r(p)>>1;
chg(p<<1|(x>mid),x,w);
up(p);
}
void add(int p,int ql,int qr,ll w){
if(ql<=l(p)&&r(p)<=qr){
v(p)+=w;
c(p)+=w;
return;
}
int mid=l(p)+r(p)>>1;
psd(p);
if(ql<=mid)add(p<<1,ql,qr,w);
if(qr>mid)add(p<<1|1,ql,qr,w);
up(p);
}
ll ask(int p,int ql,int qr){
if(ql<=l(p)&&r(p)<=qr)return v(p);
int mid=l(p)+r(p)>>1;
ll res=mn;
psd(p);
if(ql<=mid)res=max(res,ask(p<<1,ql,qr));
if(qr>mid)res=max(res,ask(p<<1|1,ql,qr));
return res;
}
}dp[2];
struct node{
int l,r,op;
ll c;
}e[N*3];
bool operator<(node a,node b){
return a.r<b.r||(a.r==b.r && a.l>b.l);
}
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=n;++i){
scanf("%lld",&e[i].c);
e[i].l=e[i].r=i;
e[i].op=0;
}
dp[0].init(n);
for(int i=1;i<=n;++i){
scanf("%lld",&e[i+n].c);
e[i+n].l=e[i+n].r=i;
e[i+n].op=1;
}
dp[1].init(n);
for(int i=2*n+1;i<=2*n+m;++i){
scanf("%d%d%d%lld",&e[i].op,&e[i].l,&e[i].r,&e[i].c);
e[i].op--;
}
m=2*n+m;
sort(e+1,e+m+1);
for(int i=1;i<=m;){
int j=i;
while(j+1<=m && e[j+1].r==e[i].r){
j++;
}
for(int k=i;k<=j;++k){
int l=e[k].l,r=e[k].r,op=e[k].op;
ll c=e[k].c;
dp[op^1].add(1,0,l-1,c);
}
ll ans[2]={mn,mn};
for(int k=i;k<=j;++k){
int l=e[k].l,r=e[k].r,op=e[k].op;
ans[op]=max(ans[op],dp[op^1].ask(1,0,l-1));
}
for(int k=0;k<2;++k){
dp[k].chg(1,e[i].r,ans[k]);
}
i=j+1;
}
printf("%lld\n",max(dp[0].ask(1,0,n),dp[1].ask(1,0,n)));
return 0;
}