Educational Codeforces Round 92 F.Bicolored Segments / 2020 计蒜之道 预赛 第一场 D.染色(困难) (线段树+dp)

题目

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

2020 计蒜之道 预赛 第一场 D.染色(困难)

题目

题解

将前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;
}

 

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小衣同学

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值