Game
思路清奇的一道数据结构题。
首先忽略字典序要求,贪心出一个最大得分。
然后用权值线段树维护这个得分,具体的说,就是在保证最大得分的基础上,调整字典序最大。
首先,我们将所有的\(a、b\)值都丢进一棵权值线段树,这样,两种值就可以互相沟通了。
在线段树向上递归的过程中维护当前区间的贡献,以及在计算贡献后剩余的\(a、b\)权值的数量。
维护贡献就是在左区间的\(b\)数量与右区间的\(a\)取\(min\)然后加上来,维护\(a、b\)数量就是将已有的\(a\)或\(b\)数量相加,然后减去作出贡献的那些。
这样在初始插入后,就可以维护出最大得分。
考虑这样为什么是贪心以及他的正确性。
在线段树单点修改时,是从叶子向上递归,那么,每一种权值的\(b\)会先遇到离自己最近的\(a\)来贡献,所以可以贪心出最大得分。
然后我们对每一个\(b\)进行二分,二分值域。
分两种二分,一种二分比他大的值,一种二分比他小的。
在能够满足最大得分的情况下,取最大值。
从前往后二分,以此来调整字典序。
具体的方法见代码。
这里证明一个事情就是:假如我二分到了一个不存在的\(a\)值,他也对答案不影响。
因为当前的\(b\)一定会对应到一个\(a\),那么,无论如何我都要在线段树中删掉一个\(a\),至于具体删哪个,并不重要,i假如我错删了一个不存在的值,他会对答案减一,但是由于我原本要删去另一个存在的\(a\)值,错删相当于他增加了一,因此对答案的减一被抵消了,不影响正确性。
代码
#include<bits/stdc++.h>
using namespace std;
namespace STD
{
#define rr register
const int N=100004;
int n,ans,pre;
int line[N];
int a[N],b[N];
multiset<int> s;
static char buf[100000],*pa=buf,*pd=buf;
#define gc pa==pd&&(pd=(pa=buf)+fread(buf,1,100000,stdin),pa==pd)?EOF:*pa++
inline int read()
{
register int x(0);register char c(gc);
while(c<'0'||c>'9')c=gc;
while(c>='0'&&c<='9')x=(x<<1)+(x<<3)+(c^48),c=gc;
return x;
}
template<typename T>
inline T cmin(rr const T x,rr const T y){return x<y?x:y;}
template<typename T>
inline T cmax(rr const T x,rr const T y){return x>y?x:y;}
#define lc id<<1
#define rc id<<1|1
struct node
{
int s;
int num[2];
node(){num[0]=num[1]=s=0;}
}t[N<<2];
void insert(const int id,const int l,const int r,const int pos,const int val,const int opt)
{
if(l==r)
{
t[id].num[opt]+=val;
return;
}
int mid=(l+r)>>1;
if(pos<=mid) insert(lc,l,mid,pos,val,opt);
else insert(rc,mid+1,r,pos,val,opt);
t[id].s=t[lc].s+t[rc].s+cmin(t[lc].num[1],t[rc].num[0]);
t[id].num[0]=t[lc].num[0]+t[rc].num[0]-cmin(t[lc].num[1],t[rc].num[0]);
t[id].num[1]=t[lc].num[1]+t[rc].num[1]-cmin(t[lc].num[1],t[rc].num[0]);
}
#undef lc
#undef rc
int find(const int val,const int opt)
{
int l,r;
if(opt)
{
r=*s.rbegin();
l=val+1;
if(l>r) return 0;
while(l<r)
{
int mid=(l+r+1)>>1;
insert(1,1,n,mid,-1,0);
if(pre+1+t[1].s==ans)
l=mid;
else r=mid-1;
insert(1,1,n,mid,1,0);
}
int temp=0;
insert(1,1,n,l,-1,0);
temp=t[1].s;
insert(1,1,n,l,1,0);
if(pre+1+temp==ans)
return l;
return 0;
}
else
{
r=val;
l=1;
while(l<r)
{
int mid=(l+r+1)>>1;
insert(1,1,n,mid,-1,0);
if(pre+t[1].s==ans) l=mid;
else r=mid-1;
insert(1,1,n,mid,1,0);
}
int temp=0;
insert(1,1,n,l,-1,0);
temp=t[1].s;
insert(1,1,n,l,1,0);
if(pre+temp==ans)
return l;
return 0;
}
}
};
using namespace STD;
int main()
{
n=read();
for(rr int i=1;i<=n;i++)
{
b[i]=read();
insert(1,1,n,b[i],1,1);
}
for(rr int i=1;i<=n;i++)
{
a[i]=read();
insert(1,1,n,a[i],1,0);
s.insert(a[i]);
}
ans=t[1].s;
for(rr int i=1;i<=n;i++)
{
insert(1,1,n,b[i],-1,1);
line[i]=find(b[i],1);
if(!line[i])
line[i]=find(b[i],0);
if(line[i]>b[i]) pre++;
insert(1,1,n,line[i],-1,0);
s.erase(s.find(line[i]));
printf("%d ",line[i]);
}
}