HDU 5649 DZY Loves Sorting(二分答案+线段树、线段树合并+线段树分割)

题意

一个 \(1\) 到 \(n\) 的全排列,\(m\) 种操作,每次将一段区间 \([l,r]\) 按升序或降序排列,求 \(m\) 次操作后的第 \(k\) 位。

\(1 \leq n \leq 10^5\)

思路

两个 \(\log\) 的做法展现了二分答案的强大功能。首先二分枚举第 \(k\) 位的值,然后将小于等于它的数都变为 \(1\) ,大于它的数变为 \(0\) ,线段树可以实现对 \(01\) 序列快速的排序,按要求进行排序,然后如果第 \(k\) 位为 \(1\) 说明这个数小于等于 \(k\) ,就这样不断二分下来,得到的边界值就是第 \(k\) 位真实的值。这个做法是离线的,有两个 \(\log\) ,但代码好实现。

但这道题,有一个 \(\log\) 、在线的做法。考虑每个位置开一棵动点线段树,把这个位置的数扔进线段树,区间的排序直接用线段树合并进行,但是如果区间的某个端点落在某一个完整的区间内,那就会破坏这个区间的单调性,所以还要线段树分割。我们对于一个完整区间,存下是升序还是降序,然后“分割”出需要的元素,线段树分割代码如下:

void split(int &x,int y,int K,int l,int r)      //y拆前K个给x,合并前将初始x清零(x是一个空树)
{
    create(x);
    if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    int mid=(l+r)>>1;
    if(K<=sum[lson[y]])
    {
        split(lson[x],lson[y],K,l,mid);
        sum[x]=sum[lson[x]]+sum[rson[x]];
        sum[y]=sum[lson[y]]+sum[rson[y]];
        return;
    }
    split(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
    lson[x]=lson[y],lson[y]=0;
    sum[x]=sum[lson[x]]+sum[rson[x]];
    sum[y]=sum[lson[y]]+sum[rson[y]];
}

和线段树合并的写法大致相同。

初始有 \(n\log n\) 个点,每次操作最多分割出 \(2\log n\) 个节点 ,所以空间复杂度为 \(O(n\log n)\)。

合并初始的 \(n\) 个节点有一个 \(n\log n\) ,而分割的节点也最多是 \(2 n\log n\) ,所以时间复杂度也是 \(O(n\log n)\)。

代码

#include<bits/stdc++.h>
#define FOR(i,x,y) for(int i=(x),i##END=(y);i<=i##END;++i)
#define DOR(i,x,y) for(int i=(x),i##END=(y);i>=i##END;--i)
typedef long long LL;
using namespace std;
const int N=1e5+5;
const int NN=N*60;
bool mmr1;
int sum[NN],lson[NN],rson[NN];
int rt[N],tot;
void build()
{
    memset(rt,0,sizeof(rt));
    sum[tot=0]=lson[0]=rson[0]=0;
}
void create(int &k){if(!k)k=++tot,sum[k]=lson[k]=rson[k]=0;}
void update(int &k,int x,int l,int r)
{
    create(k);
    sum[k]++;
    if(l==r)return;
    int mid=(l+r)>>1;
    if(x<=mid)update(lson[k],x,l,mid);
    else update(rson[k],x,mid+1,r);
}
int query1(int k,int K,int l,int r)
{
    if(l==r)
    {
        if(sum[k]!=1)return -1;
        return l;
    }
    int mid=(l+r)>>1;
    if(K<=sum[lson[k]])return query1(lson[k],K,l,mid);
    else return query1(rson[k],K-sum[lson[k]],mid+1,r);
}
int query2(int k,int K,int l,int r)
{
    if(l==r)
    {
        if(sum[k]!=1)return -1;
        return l;
    }
    int mid=(l+r)>>1;
    if(K<=sum[rson[k]])return query2(rson[k],K,mid+1,r);
    else return query2(lson[k],K-sum[rson[k]],l,mid);
}
void merge(int &x,int y,int l,int r)                        //y并进x
{
    if(!x||!y){x=(x|y);return;}
    if(l==r){sum[x]+=sum[y];return;}
    int mid=(l+r)>>1;
    merge(lson[x],lson[y],l,mid);
    merge(rson[x],rson[y],mid+1,r);
    sum[x]=sum[lson[x]]+sum[rson[x]];
}
void split1(int &x,int y,int K,int l,int r)     //y拆前K个给x
{
    create(x);
    if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    int mid=(l+r)>>1;
    if(K<=sum[lson[y]])
    {
        split1(lson[x],lson[y],K,l,mid);
        sum[x]=sum[lson[x]]+sum[rson[x]];
        sum[y]=sum[lson[y]]+sum[rson[y]];
        return;
    }
    split1(rson[x],rson[y],K-sum[lson[y]],mid+1,r);
    lson[x]=lson[y],lson[y]=0;
    sum[x]=sum[lson[x]]+sum[rson[x]];
    sum[y]=sum[lson[y]]+sum[rson[y]];
}
void split2(int &x,int y,int K,int l,int r)     //y拆后K个给x
{
    create(x);
    if(l==r){sum[x]=sum[y],sum[y]=0;return;}
    int mid=(l+r)>>1;
    if(K<=sum[rson[y]])
    {
        split2(rson[x],rson[y],K,mid+1,r);
        sum[x]=sum[lson[x]]+sum[rson[x]];
        sum[y]=sum[lson[y]]+sum[rson[y]];
        return;
    }
    split2(lson[x],lson[y],K-sum[rson[y]],l,mid);
    rson[x]=rson[y],rson[y]=0;
    sum[x]=sum[lson[x]]+sum[rson[x]];
    sum[y]=sum[lson[y]]+sum[rson[y]];
}
set<int>st;
set<int>::iterator it,it1;
bool f[N];

int find_leftmost(int x)
{
    it=st.upper_bound(x);
    return *--it;
}
int find_rightmost(int x)
{
    it=st.upper_bound(x);
    return (*it)-1;
}
bool mmr2;

int main()
{
    int T,n,m,K;
    scanf("%d",&T);
    while(T--)
    {
        build();
        st.clear();
        memset(f,0,sizeof(f));
        scanf("%d%d",&n,&m);
        FOR(i,1,n)
        {
            int x;
            scanf("%d",&x);
            update(rt[i],x,1,n);
        }
        FOR(i,1,n+1)st.insert(i);

        while(m--)
        {
            int op,l,r;
            scanf("%d%d%d",&op,&l,&r);
            int L=find_leftmost(l);
            if(l!=L)
            {
                if(f[L]==0)rt[l]=0,split1(rt[l],rt[L],l-L,1,n);
                else rt[l]=0,split2(rt[l],rt[L],l-L,1,n);
                swap(rt[l],rt[L]);
                f[l]=f[L];
                st.insert(l);
            }

            int R=find_rightmost(r),_R=find_leftmost(r);
            if(r!=R)
            {
                f[r+1]=f[_R];
                if(f[_R]==0)rt[r+1]=0,split2(rt[r+1],rt[_R],R-r,1,n);
                else rt[r+1]=0,split1(rt[r+1],rt[_R],R-r,1,n);
                st.insert(r+1);
            }

            f[l]=op;
            it=st.find(l),it++;
            while((*it)<=r)
            {
                merge(rt[l],rt[*it],1,n);
                it1=it,it++,st.erase(it1);
            }
        }
        scanf("%d",&K);
        int x=find_leftmost(K);
        if(f[x]==0)printf("%d\n",query1(rt[x],K-x+1,1,n));
        else printf("%d\n",query2(rt[x],K-x+1,1,n));
    }
    return 0;
}

原文地址:https://www.cnblogs.com/Paulliant/p/10185235.html

时间: 2024-10-13 07:14:06

HDU 5649 DZY Loves Sorting(二分答案+线段树、线段树合并+线段树分割)的相关文章

数据结构(线段树):HDU 5649 DZY Loves Sorting

DZY Loves Sorting Time Limit: 12000/6000 MS (Java/Others)    Memory Limit: 262144/262144 K (Java/Others)Total Submission(s): 294    Accepted Submission(s): 77 Problem Description DZY has a sequence a[1..n]. It is a permutation of integers 1∼n. Now he

hdu 5646 DZY Loves Partition 二分+数学分析+递推

链接:http://acm.hdu.edu.cn/showproblem.php?pid=5646 题意:将n分成k个正整数之和,要求k个数全部相同:并且这k个数的乘积最大为多少?结果mod 1e^9+7; 思路:由于是mod,不能通过模拟进行比较来判断是否为最优解:那么我们就必须直接计算出这个最优解的序列: 由于当a <= b-2时(相等时表示中间空一位),a*b < (a+1)*(b-1);所以最优解序列要不就是一串连续的序列,要不就是中间空一位,分成两段连续的序列: 因为如果存在空格超过

hdu 5195 DZY Loves Topological Sorting 线段树+拓扑排序

DZY Loves Topological Sorting Time Limit: 1 Sec  Memory Limit: 256 MB 题目连接 http://acm.hdu.edu.cn/showproblem.php?pid=5195 Description A topological sort or topological ordering of a directed graph is a linear ordering of its vertices such that for ev

hdu 5195 DZY Loves Topological Sorting BestCoder Round #35 1002 [ 拓扑排序 + 优先队列 || 线段树 ]

传送门 DZY Loves Topological Sorting Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others)Total Submission(s): 221    Accepted Submission(s): 52 Problem Description A topological sort or topological ordering of a directed

HDU 5195 DZY Loves Topological Sorting (拓扑排序+线段树)

题目地址:HDU 5195 简直受不了了..BC第二题都开始线段树+拓扑排序了... 这题很容易想到拓扑排序过程中贪心,但是贪心容易TLE,所以需要用数据结构去维护,我用的是线段树维护.每次找入度小于等于k的编号最大的点,这样就可以保证字典序一定是最大的. 代码如下: #include <iostream> #include <string.h> #include <math.h> #include <queue> #include <algorith

hdu.5195.DZY Loves Topological Sorting(topo排序 &amp;&amp; 贪心)

DZY Loves Topological Sorting Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 131072/131072 K (Java/Others) Total Submission(s): 866    Accepted Submission(s): 250 Problem Description A topological sort or topological ordering of a directed g

BZOJ 3316 JC loves Mkk 二分答案+单调队列

题目大意:给定一个环,要求在这个环上截取长度为偶数且在[L,R]区间内的一段,要求平均值最大 看到环果断倍增 看到平均值最大果断二分答案 看到长度[L,R]果断单调队列 对数组维护一个前缀和,对前缀和维护单调递增的单调队列 每扫过一个数sum[i],将sum[i-L]加入单调队列,再把距离i超过R的点删掉 长度为偶数?对奇数位置和偶数位置分别维护一个单调队列即可 每次找到大于0的子串之后记录一下分母再退出就行了 #include <cstdio> #include <cstring>

HDU 1551 Cable master【二分答案】

题意:给出n块木板,它们分别的高度,现在要把它们裁切成k块,问裁切成的最大的高度 二分答案,上限是这n块木板里面的最大值 然后每一个答案去判断一下是否满足能够裁切成k块 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include <cmath> 5 #include<stack> 6 #include<vector> 7 #include<map&

bzoj3316 JC loves Mkk 二分答案 单调队列

链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3316 题意:给出一个环,求出长度在$L~R$之间任意偶数的一个序列,使得这个序列的平均值最大. 看到分数就知道这题不可做-- 好啦言归正传--这道题应该怎么做呢--直接上$PoPoQQQ$大爷的语录: 看到环果断倍增 看到平均值最大果断二分答案 看到长度[L,R]果断单调队列 没错就是这样--平均值这个东西其实就是要这么瞎搞--二分答案,然后给每个元素"咔嚓"一下去掉二分的值,之后