线段树维护区间合并——cf1285E

感觉自己的解法又是歪的

代码写的很乱。。要先找出一开始有多少段,然后计算删掉每条线段的贡献,求个最大值就可以

删每条线段的贡献可以用线段树区间合并来做

ps:正解其实很简单。。扫描一下就可以了

/*
先把所有线段覆盖到线段树上
然后对每一个线段[L,R],查询区间[L,R]有多少值>1的段即可
*/
#include<bits/stdc++.h>
using namespace std;
#define N 800005

int n,x[N],L[N],R[N],tot;

#define lson l,m,rt<<1
#define rson m+1,r,rt<<1|1
struct Seg{
    Seg(){}
    int lazy,ltag,rtag,cnt;//左端是否>1,右端是否>1,区间>1的段数
}seg[N<<2];
void pushdown(int rt){
    seg[rt<<1].lazy+=seg[rt].lazy;
    seg[rt<<1|1].lazy+=seg[rt].lazy;
    seg[rt].lazy=0;
}
void pushup(int rt){
    seg[rt].ltag=seg[rt<<1].ltag;
    seg[rt].rtag=seg[rt<<1|1].rtag;
    seg[rt].cnt=seg[rt<<1].cnt+seg[rt<<1|1].cnt;
    if(seg[rt<<1].rtag && seg[rt<<1|1].ltag)
        seg[rt].cnt--;
}
Seg merge(Seg a,Seg b){
    Seg res;
    res.cnt=a.cnt+b.cnt;
    res.ltag=a.ltag;
    res.rtag=b.rtag;
    if(a.rtag && b.ltag)res.cnt--;
    return res;
}
void build(int l,int r,int rt){
    seg[rt].cnt=seg[rt].lazy=seg[rt].ltag=seg[rt].rtag=0;
    if(l==r)return;
    int m=l+r>>1;
    build(lson);build(rson);
}
void update(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r){seg[rt].lazy++;return;}
    pushdown(rt);
    int m=l+r>>1;
    if(L<=m)update(L,R,lson);
    if(R>m)update(L,R,rson);
}
void rollback(int l,int r,int rt){
    if(l==r){
        if(seg[rt].lazy>1)
            seg[rt].ltag=seg[rt].rtag=seg[rt].cnt=1;
        return;
    }
    pushdown(rt);
    int m=l+r>>1;
    rollback(lson);rollback(rson);
    pushup(rt);
}
Seg query(int L,int R,int l,int r,int rt){
    if(L<=l && R>=r)return seg[rt];
    int m=l+r>>1,flag=0;
    Seg res;
    if(L<=m)res=query(L,R,lson),flag=1;
    if(R>m){
        if(flag)
            res=merge(res,query(L,R,rson));
        else res=query(L,R,rson);
    }
    return res;
}
void init(){
    tot=0;
}
void debug(int l,int r,int rt){
    cout<<l<<" "<<r<<" "<<rt<<" "<<seg[rt].cnt<<" "<<seg[rt].lazy<<" "<<seg[rt].rtag<<‘\n‘;
    if(l==r)return;
    int m=l+r>>1;
    debug(lson);debug(rson);
}

int cnt[N];
int main(){
    int T;cin>>T;
    while(T--){
        cin>>n;
        init();
        for(int i=1;i<=n;i++){
            scanf("%d%d",&L[i],&R[i]);
            x[++tot]=L[i];x[++tot]=R[i];
        }
        sort(x+1,x+1+tot);
        tot=unique(x+1,x+1+tot)-x-1;
        for(int i=1;i<=n;i++){
            L[i]=lower_bound(x+1,x+1+tot,L[i])-x;
            R[i]=lower_bound(x+1,x+1+tot,R[i])-x;
            L[i]<<=1;R[i]<<=1;
        }
        tot<<=1;
        build(1,tot,1);
        for(int i=1;i<=n;i++){
            update(L[i],R[i],1,tot,1);
            //debug(1,tot,1);
        }
        rollback(1,tot,1);//求出每段的1的个数 

        for(int i=1;i<=tot;i++)cnt[i]=0;
        for(int i=1;i<=n;i++)cnt[L[i]]++,cnt[R[i]+1]--;
        for(int i=1;i<=tot;i++)cnt[i]+=cnt[i-1];
        int len=0;
        for(int i=1;i<=tot;i++)
            if(cnt[i] && cnt[i-1]==0)len++;

        int Max=-0x3f3f3f3f;
        for(int i=1;i<=n;i++)
            Max=max(Max,query(L[i],R[i],1,tot,1).cnt-1);
        cout<<Max+len<<‘\n‘;
    }
} 

原文地址:https://www.cnblogs.com/zsben991126/p/12273691.html

时间: 2024-10-09 16:52:41

线段树维护区间合并——cf1285E的相关文章

HDU 1540 &amp;&amp; POJ 2892 Tunnel Warfare (线段树,区间合并).

~~~~ 第一次遇到线段树合并的题,又被律爷教做人.TAT. ~~~~ 线段树的题意都很好理解吧.. 题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=1540 http://poj.org/problem?id=2892 ~~~~ 我的代码:200ms #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> #defin

线段树维护区间最大子段和

线段树:我还是很强的 简略讲解 要用线段树维护区间,我们要明确: 线段树存什么东西 怎么合并 如果有区间修改,怎么打标记 对于区间最大子段和,我们可以记录四个值:以维护的区间左端点为起点的最大子段和,以维护的区间右端点为终点的最大子段和,在维护区间内的最大子段和 和维护区间所有元素的和 合并的话稍微麻烦一些,看代码吧: inline void up(int p){ tree[p].sum=tree[ls].sum+tree[rs].sum; //维护区间总和 tree[p].ll=max(tre

POJ 3667 线段树的区间合并简单问题

题目大意:有一排标号1-N的房间.操作一:询问是不是有连续长度为a的空房间,有的话住进最左边(占用a个房间)操作二:将[a,a+b-1]的房间清空(腾出b个房间)思路:记录每个区间中“靠左”“靠右”“中间”的空房间线段树操作:update:区间替换query:询问满足条件的最左端点 题目链接: http://vjudge.net/problem/viewProblem.action?id=10354 题目操作: 因为这里找从最左边住起的房间,所以这里不能像常规地写query函数那样写了,终于发现

HDU 3308 线段树(区间合并)

LCIS Time Limit: 6000/2000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3896    Accepted Submission(s): 1766 Problem Description Given n integers.You have two operations:U A B: replace the Ath number by B. (index

最敏捷的机器人(线段树维护区间最值)

题面: Wind设计了很多机器人.但是它们都认为自己是最强的,于是,一场比赛开始了--机器人们都想知道谁是最敏捷的,于是它们进行了如下一个比赛.首先,他们面前会有一排共n个数,它们比赛看谁能最先把每连续k个数中最大和最小值写下来,当然,这些机器人运算速度都很,它们比赛的是谁写得快.但是Wind也想知道答案,你能帮助他吗? Input: 每组测试数据 第1行为n,k(1<=k<=n<=100000) 第2行共n个数,为数字序列,所有数字均在int范围内. Output: 共n-k+1行 第

线段树维护区间开方/除法

今天考试考了一些神仙数据结构 T1 线段树维护区间加,区间开方,区间和 (数据范围:5e5) T2 线段树维护区间加,区间除,区间和,区间最值 对于这些题目,就像是之前考的区间与,区间或一样,除法,开方的操作会让各个数字之间越来越相近,最后变成一串一串连续的数字都是一样的,所以对于这一部分的操作我们一定程度上使用暴力,而如果一段都相等就相当于直接进行区间剪发的操作 那么我们来看如何判断区间一段都相等,那我们只需要维护区间的最值,最小值==最大值就完全相等了 然后.....代码.....被 \(y

线段树维护区间最大子段和 枚举 HDU6638

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6638 题意:在一个二维坐标系上给你n(n<=2000)个点,点带有一个价值w(有正有负),点的坐标都在(-1e9,1e9)的范围之间,可任意用一个平行于坐标轴的矩形框住一片区域,求这片区域框住的点的价值和 分析:点的坐标范围太大,离散化应能想到.离散化后可以考虑枚举左边界,枚举左边界后按照横坐标的依次加点(以一列一列为单位),用线段树维护一列的最大子段和,每移动到新的一列,继续加点时,等价于向原先的

[HDOJ3308]LCIS(线段树,区间合并)

题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=3308 题意:给定n个数,两个操作: U A B:将位置A的数值改成B Q A B:查询[A,B]内最长连续上升子序列的长度. 注意到‘连续’一词,可以用线段树维护[L,R]区间内的LICS. 定义结构Node,内部ls,rs为左右儿子的下标.l,r记录当前区间分别从左起和右起的LICS长度,s记录整个区间内的LICS长度. pushup:和一般的区间合并操作一样,但是要注意假如合并的左右子树中间有可

Codeforces Round #222 (Div. 1) D. Developing Game 线段树有效区间合并

D. Developing Game Pavel is going to make a game of his dream. However, he knows that he can't make it on his own so he founded a development company and hired n workers of staff. Now he wants to pick n workers from the staff who will be directly res