P2824 [HEOI2016/TJOI2016]排序 妙啊 思维 二分 线段树

P2824 [HEOI2016/TJOI2016]排序

贴个链接
题意是给一个长度为n的序列,有m次操作;每次操作是:
0 l r 把 l ~ r 区间按递增排序
1 l r 把 l ~ r 区间按递减排序
最后为pos 位置上的值是多少。
这个怎么做?
按他的操作没法完成排序,(是我不会)
但是,可以考虑二分一个答案。
现在问题相当于是给一个值问经过上次操作后的值比这个值大还是比这个值小。来确定二分边界的变化。
?把比mid小的变为0,把mid大的变为1.
这样一来,排序操作就是查询这个区间有多少个1,然后把前面一段变成1,后面一段变成0就好,就是简单的线段树。
最后查询这个位置上的值是0还是1 是1的话证明答案比它大,否则比他小。
所以复杂度nlognlogn

代码

#include <algorithm>
#include <iostream>
#include <cstdio>
#include <string>
#include <queue>
#include <cstring>
#include <stack>
#include <map>
#include <bitset>
#include <math.h>
#include <set>
#include <ctime>
#include <unordered_set>
#include <climits>
using namespace std;
typedef long long ll;
typedef pair<int,ll> pii;
typedef pair<ll,ll> pll;
typedef pair<double,double> pdd;
typedef unsigned long long ull;
typedef unordered_set<int>::iterator sit;
#define st first
#define sd second
#define mkp make_pair
#define pb push_back
void tempwj(){freopen("P3380_2.in","r",stdin);freopen("hash.out","w",stdout);}
ll gcd(ll a,ll b){return b == 0 ? a : gcd(b,a % b);}
ll qpow(ll a,ll b,ll mod){a %= mod;ll ans = 1;while(b > 0){if(b & 1)ans = ans * a % mod;a = a * a % mod;b >>= 1;}return ans % mod;}
struct cmp{bool operator()(const pii & a, const pii & b){return a.second > b.second;}};
int lb(int x){return  x & -x;}
// friend bool operator < (Node a,Node b)   重载
// 8388607
const int inf = INT_MAX;
const double esp = 1e-9;
const ll INF = 0x3f3f3f3f3f3f;
const ll mod = 1e9+7;
const int maxn = 1e5 + 5;
const int M = 2e6+5;
int a[maxn];
int b[maxn];
int c[maxn];

int n,m;
struct Node
{
    int l,r,num,tag;
}node[maxn << 2];

void build(int l,int r,int no)
{
    node[no].tag = -1;
    node[no].l = l;
    node[no].r = r;
    node[no].num = 0;
    if(l == r)
    {
        node[no].num = c[l];
        return;
    }
    int mid = l +r >> 1;
    build(l,mid,no<<1);
    build(mid+1,r,no<<1|1);
    node[no].num = node[no<<1].num + node[no<<1|1].num;
}

void change(int no,int num)
{
    node[no].num = (node[no].r - node[no].l + 1) * num;
    node[no].tag = num;
}
void down(int no)
{
    change(no<<1,node[no].tag);
    change(no<<1|1,node[no].tag);
    node[no].tag = -1;
}


void update(int l,int r,int no,int num)
{
    if(node[no].l > r|| node[no].r < l)
        return;
    if(node[no].l >=l && node[no].r <= r)
    {
        change(no,num);
        return;
    }
    if(node[no].tag != -1)
        down(no);
    update(l,r,no<<1,num);
    update(l,r,no<<1|1,num);
    node[no].num = node[no<<1].num + node[no<<1|1].num;
}
int query(int l,int r,int no)
{
    if(node[no].l > r || node[no].r < l)
        return 0;
    if(node[no].l >= l && node[no].r <= r)
        return node[no].num;
    if(node[no].tag != -1)
        down(no);
    return query(l,r,no<<1) + query(l,r,no<<1|1);
}
struct Que
{
    int f,x,y;
}Q[maxn];
int k;
bool solve(int x)
{
    // printf("%d\n",x);
    for (int i = 1; i <= n; i ++ )
    {
        if(a[i] >= x)
            c[i] = 1;
        else
            c[i] = 0;
        // printf("%d ",c[i]);
    }
    // printf("\n");
    build(1,n,1);
    for (int  i= 1; i <= m; i ++ )
    {
        int s= 0 ;
        if(Q[i].f == 1) // 降序 1111111 00000 
        {
            s = query(Q[i].x,Q[i].y,1);
            update(Q[i].x,Q[i].x + s - 1,1,1);
            update(Q[i].x + s,Q[i].y,1,0);
        }
        else // 00001111
        {
            s = query(Q[i].x,Q[i].y,1);
            update(Q[i].x,Q[i].y - s,1,0);
            update(Q[i].y - s + 1,Q[i].y,1,1);
        }
        // printf("%d %d %d\n",Q[i].x,Q[i].y,s);
    }
    int s = query(k,k,1);
    return s == 1;
}


int main()
{
    
    scanf("%d%d",&n,&m);
    for (int  i=1 ; i <= n; i ++ )
    {
        scanf("%d",&a[i]);
        b[i] = a[i];
    }
    for(int i =1 ; i <= m; i ++ )
    {
        scanf("%d%d%d",&Q[i].f,&Q[i].x,&Q[i].y);
    }
    sort(b + 1, b + 1 + n);
    // int k;
    scanf("%d",&k);
    int l = 1, r = n;
    while(l < r)
    {
        int mid = l +r + 1>> 1;
        if(solve(mid))
            l = mid;
        else
            r = mid - 1;
    }
    printf("%d\n",b[l]);
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值