Codeforces 1389 F. Bicolored Segments —— DP+线段树,有丶东西

96 篇文章 1 订阅
这篇博客介绍了如何使用线段树数据结构解决一个数学问题:在给定不同颜色的线段中,最多能选择多少条线段,使得它们之间没有任何一对线段相交且颜色不同。博主通过三种思路最终得出解决方案,并详细解释了线段树的更新和查询操作,以及代码实现过程。
摘要由CSDN通过智能技术生成

This way

题意:

现在有n个线段,每个线段有一种颜色,定义一对线段是“坏的”,当且仅当这对线段i,j满足以下条件:
1.c[i]!=c[j]
2.i和j相交(包括边界)
问你最多能选择多少线段使得其中没有任何一对线段是坏的

题解:

先后想了三种方法,花了我两个半小时
接下来假设颜色为0,1
首先我得到一个结论,就是c[i]只会从c[i]^1的地方转移过来。
因为如果从相同颜色的地方转移过来,会有很多很烦的情况(或许是我菜了),刷掉了我前两个想法。
那么怎么找这个转移的位置,可以用线段树来查询最大值。
因为我们是从相反颜色转移过来的,所以这个转移的位置后面可能会有一些相同颜色的线段,当然这两个要合在一起才能查询最大值。
那么我们只需要在这个做之前,对c[i]^1这棵线段树的0~e[i].l-1区间+1即可。
最后还需要更新c[i]线段树的e[i].r位置。
当然这是两种更新,我们需要不同处理方法:
在这里插入图片描述
有人可能会问,为什么在op=1的时候,不需要加上那些+1的线段。
因为我是按右端点排序的,所以+1的那些位置一定不会影响到后面的点,因此不需要处理这种情况。

#include<bits/stdc++.h>
using namespace std;
#define pa pair<int,int>
const int N=6e5+5;
struct Segment_Tree{
    int mx[N*4],ad[N*4],f[N*4];
    void push_down(int root){
        if(!f[root])return ;
        mx[root<<1]+=f[root];
        mx[root<<1|1]+=f[root];
        f[root<<1]+=f[root];
        f[root<<1|1]+=f[root];
        f[root]=0;
    }
    void update(int l,int r,int root,int ql,int qr,int op,int v){
        if(l>=ql&&r<=qr){
            if(op==1)
                mx[root]=max(mx[root],v);
            else
                mx[root]+=v,f[root]+=v;
            return ;
        }
        push_down(root);
        int mid=l+r>>1;
        if(mid>=ql)
            update(l,mid,root<<1,ql,qr,op,v);
        if(mid<qr)
            update(mid+1,r,root<<1|1,ql,qr,op,v);
        mx[root]=max(mx[root<<1],mx[root<<1|1]);
    }
    int query(int l,int r,int root,int ql,int qr){
        if(l>=ql&&r<=qr)return mx[root];
        push_down(root);
        int mid=l+r>>1;
        int ans=0;
        if(mid>=ql)
            ans=query(l,mid,root<<1,ql,qr);
        if(mid<qr)
            ans=max(ans,query(mid+1,r,root<<1|1,ql,qr));
        return ans;
    }
}t[2];
struct edge{
    int l,r,c;
    bool operator< (const edge& a)const {
        return r<a.r;
    }
}e[N];
int a[N*2];
int main()
{
    int n,tot=0;
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
        scanf("%d%d%d",&e[i].l,&e[i].r,&e[i].c),e[i].c--,a[++tot]=e[i].l,a[++tot]=e[i].r;
    sort(a+1,a+1+tot);
    tot=unique(a+1,a+1+tot)-a-1;
    sort(e+1,e+1+n);
    for(int i=1;i<=n;i++){
        e[i].l=lower_bound(a+1,a+1+tot,e[i].l)-a;
        e[i].r=lower_bound(a+1,a+1+tot,e[i].r)-a;
    }
    int ans=0,val,num[2]={0};
    for(int i=1;i<=n;i++){
        int col=e[i].c;
        num[col]++;
        t[col^1].update(0,tot,1,0,e[i].l-1,2,1);
        val=0;
        val=t[col^1].query(0,tot,1,0,e[i].l-1);
        val=max(num[col],val);
        t[col].update(0,tot,1,e[i].r,e[i].r,1,val);
        ans=max(ans,val);
    }
    printf("%d\n",ans);
    return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值