uoj#280. 【UTR #2】题目难度提升(构造)

本文探讨了一种算法策略,用于解决在序列中维护中位数并确保操作后的字典序最大的问题。通过排序、分情况讨论及使用对顶堆,实现了在不同场景下高效更新中位数,保持字典序最大特性。

传送门

咱先膜一下\(GXZ\)再说

我们先把序列从小到大排序,然后分情况讨论

无解是不存在的,从小到大输出所有数肯定可行

情况一,如果\(a[mid]=a[mid+1]\),因为最终的中位数也是它们,那么我们可以让中位数一直等于\(a[mid]\),找到最大的\(k\)满足\(a[k]=a[mid]\),那么就先删\(k\),然后再删\(k-1,n,k-2,n-1,...\)那么显然这个过程中中位数始终为\(a[mid]\),且满足字典序最大

情况二,如果\(a[mid]\neq a[mid+1]\)且存在\(k<\lceil\frac n2\rceil,a_k=a_{k+1}\),那么我们可以从\(k\)开始取,然后按\(k-1,n,k-2,n-1,...\)的顺序取,最后前面的数一定会被取完,而且这个过程中中位数不变

情况三,第一个数只能取\(a[1]\),因为很明显删数的过程中要始终满足删的数不能小于已删的数的中位数否则\(GG\)

于是维护一个叫做对顶堆的东西,简单来说就是把小的数放一个大根堆里,大的数放一个小根堆里,并保证两堆的元素个数之差不超过\(1\),那么中位数只要从两个堆顶取就行了

对于剩下的所有未删除的数,因为按照我们上面的做法,情况一是已经做完了,情况二和情况三都会删到只剩一个连续的区间,且这个区间的中位数大于等于已经删的数的中位数,于是每一次加入能使中位数大于等于剩下的最小的数的就行了

//minamoto
#include<bits/stdc++.h>
#define R register
#define IT set<int>::iterator
#define fp(i,a,b) for(R int i=a,I=b+1;i<I;++i)
#define fd(i,a,b) for(R int i=a,I=b-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R int x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]=' ';
}
const int N=1e5+5;
multiset<int>s;priority_queue<int>A,B;
int a[N],vis[N],n;
void push(int x){
    if(A.empty()||x<=A.top())A.push(x);
    else B.push(-x);
    if(A.size()<B.size())A.push(-B.top()),B.pop();
    if(A.size()-B.size()>1)B.push(-A.top()),A.pop();
}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read();
    fp(i,1,n)a[i]=read();
    sort(a+1,a+1+n);
    int mid=(n+1)>>1;
    if(a[mid]==a[mid+1]){
        while(mid<n&&a[mid]==a[mid+1])++mid;
        print(a[mid]);
        int p=mid-1,q=n;
        while(p||q>mid){
//          printf("%d %d\n",p,q);
            if(p)print(a[p--]);
            if(q>mid)print(a[q--]);
        }return Ot(),0;
    }
    while(mid>1&&a[mid]!=a[mid-1])--mid;
    print(a[mid]),vis[mid]=1;
    int p=mid-1,q=n;IT it;
    while(p&&q>mid)print(a[p]),print(a[q]),vis[p--]=vis[q--]=1;
    fp(i,1,n)if(vis[i])push(a[i]);else s.insert(a[i]);
    while(!s.empty()){
        p=*s.begin();
        if(A.size()==B.size())it=(p>=-B.top())?--s.end():s.begin();
        else{
            if(!B.empty()&&p*2>=A.top()-B.top())it=--s.end();
            else it=--s.upper_bound(p*2-A.top());
        }
        print(*it),push(*it),s.erase(it);
    }
    return Ot(),0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10287667.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值