UESTC #1919 一棵复杂的线段树

Description

给一个\(1 \sim n\)的排列,进行\(m\)次操作,可以将一个区间\([l,r]\)内的数升序排序或者降序排序,最后进行一次询问问第\(k\)个数字为多少。

Solution

二分答案,对于每一个二分的值\(x\),将原排列中小于等于\(x\)的数视为\(0\),大于\(x\)的树视为\(1\),用线段树维护,排序操作可以用线段树的区间赋值实现。

Code

/*
 Author: LargeDumpling
 Email: [email protected]
 Edit History:
    2018-07-24  File created.
*/

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int MAXN=100050;
struct jz
{
    int l,r,typ;
}O[MAXN];
int num[MAXN],sum[MAXN<<2],tag[MAXN<<2],L[MAXN<<2],R[MAXN<<2],n,m,k;
void maintain(int root)
{
    sum[root]=sum[root<<1]+sum[root<<1|1];
    return;
}
void build(int root,int l,int r,int x)
{
    L[root]=l; R[root]=r; tag[root]=-1;
    if(l==r)
    {
        sum[root]=(num[l]<=x)?0:1;
        return;
    }
    int mid=(l+r)>>1;
    build(root<<1,l,mid,x);
    build(root<<1|1,mid+1,r,x);
    maintain(root);
    return;
}
void down(int root)
{
    if(tag[root]==-1) return;
    sum[root<<1]=tag[root]*(R[root<<1]-L[root<<1]+1);
    tag[root<<1]=tag[root];
    sum[root<<1|1]=tag[root]*(R[root<<1|1]-L[root<<1|1]+1);
    tag[root<<1|1]=tag[root];
    tag[root]=-1;
    return;
}
void change(int root,int l,int r,int x)
{
    if(r<l) return; //because of line 98, 99, 103, 104, ignore this will cause a Wrong Answer.
    if(l<=L[root]&&R[root]<=r)
    {
        sum[root]=x*(R[root]-L[root]+1);
        tag[root]=x;
        return;
    }
    down(root);
    int mid=(L[root]+R[root])>>1;
    if(l<=mid) change(root<<1,l,r,x);
    if(mid<r) change(root<<1|1,l,r,x);
    /*if(l<=R[root<<1]) change(root<<1,l,r,x);
    if(R[root<<1]<r) change(root<<1|1,l,r,x);*/ //this will cause a Runtime Error.
    maintain(root);
    return;
}
int query(int root,int l,int r)
{
    if(r<l) return 0;
    if(l<=L[root]&&R[root]<=r) return sum[root];
    down(root);
    int ans=0,mid=(L[root]+R[root])>>1;
    if(l<=mid) ans+=query(root<<1,l,r);
    if(mid<r) ans+=query(root<<1|1,l,r);
    /*if(l<=R[root<<1]) ans+=query(root<<1,l,r);
    if(R[root<<1]<r) ans+=query(root<<1|1,l,r);*/
    return ans;
}
void read1n(int &x)
{
    char ch;
    for(ch=getchar();ch<'0'||'9'<ch;ch=getchar());
    for(x=0;'0'<=ch&&ch<='9';ch=getchar())
        x=(x<<1)+(x<<3)+ch-'0';
    return;
}
bool check(int x)
{
    int cnt0,cnt1;
    build(1,1,n,x);
    for(int i=1;i<=m;i++)
    {
        cnt1=query(1,O[i].l,O[i].r);
        cnt0=O[i].r-O[i].l+1-cnt1;
        if(O[i].typ)
        {
            change(1,O[i].l,O[i].l+cnt1-1,1);
            change(1,O[i].r-cnt0+1,O[i].r,0);
        }
        else
        {
            change(1,O[i].l,O[i].l+cnt0-1,0);
            change(1,O[i].r-cnt1+1,O[i].r,1);
        }
    }
    return query(1,k,k)==0;
}
int main()
{
    int l,r,mid;
    read1n(n); read1n(k);
    for(int i=1;i<=n;i++)
        read1n(num[i]);
    read1n(m);
    for(int i=1;i<=m;i++)
    {
        read1n(O[i].typ);
        read1n(O[i].l);
        read1n(O[i].r);
    }
    l=0; r=n;
    while(l<r-1)
    {
        mid=(l+r)>>1;
        if(check(mid)) r=mid;
        else l=mid;
    }
    printf("%d\n",r);
    fclose(stdin);
    fclose(stdout);
    return 0;
}

Other thing

傅大爷数据出的太好了,把我原来线段树的写法卡疯了。

猜你喜欢

转载自www.cnblogs.com/LargeDumpling/p/9362321.html