线段树的灵活运用--2018 UESTC Training for Data Structures一棵复杂的线段树 UESTC - 1919

时间:2022-11-12 10:19:43

题意:给一个1到n的某种排列 每次有两种操作 将一个区间正向排序或者方向排序 问最后第k位数字是什么 n和操作次数最大是10W

这题挺难的,关键在于利用线段树的排序功能

因为他没有规律的变换某个区间的次序,所以很难找到某个位置的通解,所以我们可以用二分枚举猜一个答案,此时我们把大于或等于这个答案的数置为1 小于这个答案的数置为0 这样我们就可以通过线段树的区间替换功能来维护一个区间有多少个1 多少个0 当操作是正向排序是 就将所有0放到前面 当操作是反向排序是 就将所有1放到前面 最后看第k位 如果是1的话 说明答案是大于或者是等于当前假设的答案的 否则就是小于当前答案的 这样就可以巧妙的解决这道题了

这题关键要想到 虽然在数字各不相同的时候 线段树没办法在log(n)内将某个区间排好序 但是如果1到n只有0和1的话线段树就可以通过区间的替换在log(n)内把某个区间排好序 

码的时候不小心把懒惰标记设为0 疯狂WA 1(ORZ   事实证明不要太依赖模板 不然很可能码的时候会被带偏

#include <set>
#include <map>
#include <list>
#include <deque>
#include <cmath>
#include <queue>
#include <stack>
#include <bitset>
#include <vector>
#include <string>
#include <cstdio>
#include <cstdlib>
#include <iomanip>
#include <cstring>
#include <fstream>
#include <iostream>
#include <algorithm>
#pragma comment(linker, "/STACK:1024000000,1024000000")
using namespace std;

const int maxn=100005;
int a[maxn],n,k,t,mid;
int sum[maxn<<2];
int lazy[maxn<<2];
int nxt;

void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void pushdown(int rt,int v)
{
    if(lazy[rt]!=-1)
    {
        lazy[rt<<1]=lazy[rt<<1|1]=lazy[rt];
        sum[rt<<1]=(v-(v>>1))*lazy[rt];
        sum[rt<<1|1]=(v>>1)*lazy[rt];
        lazy[rt]=-1;
    }
}
void build(int l,int r,int rt)
{
    lazy[rt]=-1;
    if(l==r)
    {
        nxt++;
        if(a[nxt]>=mid)
            sum[rt]=1;
        else
            sum[rt]=0;
        return;
    }
    int m=l+(r-l)/2;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}

int query(int le,int re,int l,int r,int rt)
{
    if(le<=l&&re>=r)
    {
        return sum[rt];
    }
    pushdown(rt,r-l+1);
    int m=l+(r-l)/2;
    int ret=0;
    if(le<=m) ret+=query(le,re,l,m,rt<<1);
    if(re>m) ret+=query(le,re,m+1,r,rt<<1|1);
    return ret;
}
void update(int le,int re,int l,int r,int rt,int c)
{
    if(le<=l&&re>=r)
    {
        lazy[rt]=c;
        sum[rt]=c*(r-l+1);
        return;
    }
    pushdown(rt,r-l+1);
    int m=l+(r-l)/2;
    if(le<=m) update(le,re,l,m,rt<<1,c);
    if(re>m) update(le,re,m+1,r,rt<<1|1,c);
    pushup(rt);
}

int opi[maxn];
int li[maxn];
int ri[maxn];

void display()
{
    cout<<mid<<endl;
    for(int i=1; i<=9; i++)
    {
        cout<<i<<": "<<sum[i]<<endl;
    }
    cout<<endl;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1; i<=n; i++)
    {
        scanf("%d",&a[i]);
    }
    scanf("%d",&t);
    for(int i=1; i<=t; i++)
    {
        scanf("%d%d%d",&opi[i],&li[i],&ri[i]);
    }
    int x=1,y=n;
    while(x<y)
    {
        nxt=0;
        mid=x+(y-x)/2;
        mid++;
        build(1,n,1);
        mid--;
        for(int i=1; i<=t; i++)
        {
            int op;
            op=opi[i];
            if(op==1)
            {
                int le=li[i],re=ri[i];
                int num=query(le,re,1,n,1);
                if(num==0) continue;
                update(le,le+num-1,1,n,1,1);
                update(le+num,re,1,n,1,0);
            }
            else
            {
                int le=li[i],re=ri[i];
                int num=query(le,re,1,n,1);
                num=re-le+1-num;
                if(num==0) continue;
                update(le,le+num-1,1,n,1,0);
                update(le+num,re,1,n,1,1);
            }
        }
        if(query(k,k,1,n,1)==1) x=mid+1;
        else y=mid;
    }
    printf("%d\n",x);
    return 0;
}
/*
5 3
5 2 1 3 4
3
1 1 3
0 2 4
1 3 5

8 3
1 5 4 2 3 8 7 6
2
1 3 5
1 2 6
*/