[计蒜客2019年8月提高组模拟赛]连续段

题目大意

\(n\)个集合,每次会将一个区间\([l,r]\)中的所有集合加入一个数\(x\)

最后询问,每个集合的最大连续段。连续段就是一个区间的元素都在一个集合中出现。

解题思路

考虑所有操作按\(x\)排序,所有我们只需要考虑操作的连续性即可。

对于连续性断开的区间我们区间赋\(0\),否则就是区间\(+1\),最后询问一个位置的历史最大值。

直接套用jls的线段树维护历史极值的操作即可。复杂度\(O(nlogn)\)

还有一种就是按位置排序然后扫描线。

#include<cstdio>
#include<algorithm>
#define ls (x<<1)
#define rs ((x<<1)+1)
#define LL long long
using namespace std;
const int  maxn=100005,INF=1e9;
inline int _read(){
    int num=0;char ch=getchar();
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') num=num*10+ch-48,ch=getchar();
    return num;
}
struct jz{
    int x,y;
    jz(int x=0,int y=-INF):x(x),y(y){}
    jz operator+(const jz &b)const{return jz(max(-INF,x+b.x),max(y+b.x,b.y));}
    jz operator*(const jz &b)const{return jz(max(x,b.x),max(y,b.y));}
}tg[maxn];
struct node{
    int x,l,r;
    bool operator<(const node &b)const{
        if (x==b.x) return l<b.l;
        return x<b.x;
    }
}c[maxn];
struct data{
    int x1,x2;
    jz add,pre;
}a[maxn*4];
int n,m,ans[maxn];
int f(int x,jz w){return max(x+w.x,w.y);}
void Pushdown(int x){
    jz add=a[x].add,pre=a[x].pre;a[x].add=a[x].pre=jz();
    int L=2*x,R=2*x+1;
    a[L].pre=a[L].pre*(a[L].add+pre);a[L].add=a[L].add+add;
    a[R].pre=a[R].pre*(a[R].add+pre);a[R].add=a[R].add+add;
    a[L].x2=max(a[L].x2,f(a[L].x1,pre));a[L].x1=f(a[L].x1,add);
    a[R].x2=max(a[R].x2,f(a[R].x1,pre));a[R].x1=f(a[R].x1,add);
}
void Pushup(int x){
    a[x].x1=max(a[ls].x1,a[rs].x1);
    a[x].x2=max(a[ls].x2,a[rs].x2);
}
void add(int x,int l,int r,int L,int R,jz w){
    if (l<r) Pushdown(x);
    if (l==L&&r==R){
        a[x].pre=a[x].pre*(a[x].add+w);
        a[x].add=a[x].add+w;
        a[x].x2=max(a[x].x2,f(a[x].x1,w));a[x].x1=f(a[x].x1,w);
        return;
    }
    int mid=l+(r-l>>1);
    if (R<=mid) add(ls,l,mid,L,R,w);else
    if (L>mid) add(rs,mid+1,r,L,R,w);else
    add(ls,l,mid,L,mid,w),add(rs,mid+1,r,mid+1,R,w);
    Pushup(x);
}
void Pushdown(int x,int l,int r){
    if (l<r) Pushdown(x);
    if (l==r){printf("%d ",a[x].x2);return;}
    int mid=l+(r-l>>1);
    Pushdown(ls,l,mid);Pushdown(rs,mid+1,r);
    Pushup(x);
}
void work(){
    int i=1,j,pd,now;
    while(i<=m){
        j=i;pd=0;now=0;while(j<=m&&c[j].x==c[i].x) j++;
        if (c[i].x>c[i-1].x+1) add(1,1,n,1,n,jz(-INF,0));else pd=1;
        for (int k=i;k<j;k++){
            if (pd&&now+1<=c[k].l-1) add(1,1,n,now+1,c[k].l-1,jz(-INF,0));
            add(1,1,n,c[k].l,c[k].r,jz(1,-INF));now=c[k].r;
        }
        if (pd&&now+1<=n) add(1,1,n,now+1,n,jz(-INF,0));i=j;
    }
}
int main(){
    freopen("exam.in","r",stdin);
    freopen("exam.out","w",stdout);
    n=_read();m=_read();
    for (int i=1;i<=m;i++) c[i].l=_read(),c[i].r=_read(),c[i].x=_read();
    sort(c+1,c+1+m);work();Pushdown(1,1,n);
    return 0;
}

转载于:https://www.cnblogs.com/CHNJZ/p/11487069.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值