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]);
	}
}
posted @ 2021-08-06 16:40  Geek_kay  阅读(70)  评论(0编辑  收藏  举报