题解 Game

传送门

一有「字典序最大」什么的的就懵了……这题我颓的std

首先可以发现全局最大得分很好统计,我们令它为 \(k\)
然后我们尝试构造方案,但发现无论怎么放都可能会有后效性
发现对于一个位置,可以放在这里的数有单调性
也即如果有一个比较大的数放在了这里,后面可能就匹配不够 \(k\) 个了
所以我们可以check一下, 对每个位置分两种情况

  1. 我们找一个 \(a\) 与 \(b[i]\) 形成匹配:
    那这时我们二分一个 \(a\) ,check一下去掉这个 \(a\) 后后面还能不能形成 \(k-1\) 个匹配
    如果check最后二分出的结果也不能满足,说明这个地方就不能匹配

  2. 我们找一个 \(a\) ,但它无法与 \(b[i]\) 形成匹配:
    二分一个最大的 \(a\) ,且删掉这个 \(a\) 后后面仍能形成 \(k\) 个匹配

然后就是如何check了……可以 \(O(n)\) 跑check
神仙思路:开一棵权值线段树,分别维护这段值域中剩余未匹配a, b的个数及匹配的数量
因为右区间的a一定可以匹配左区间的b,所以这个东西可以合并
然后就每次单点修改删掉一个数,看根节点的匹配个数就行了

  • 处理「字典序最大/小的合法方案」(?):如果每个位置的取值有单调性,二分找这里能放的,且这里放了后面仍能满足要求的最大的取值。其实这样的话难点在于check

Code:

#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define ll long long 
//#define int long long 

char buf[1<<21], *p1=buf, *p2=buf;
#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf, 1, 1<<21, stdin)), p1==p2?EOF:*p1++)
inline int read() {
	int ans=0, f=1; char c=getchar();
	while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
	while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
	return ans*f;
}

int n;
int a[N], b[N];

namespace force{
	int ans[N], maxn;
	void solve() {
		sort(a+1, a+n+1);
		do {
			int cnt=0;
			for (int i=1; i<=n; ++i) if (a[i]>b[i]) ++cnt;
			if (cnt>=maxn) memcpy(ans+1, a+1, sizeof(int)*n), maxn=cnt;
		} while (next_permutation(a+1, a+n+1));
		for (int i=1; i<=n; ++i) printf("%d ", ans[i]);
		printf("\n");
		exit(0);
	}
}

namespace task1{
	int a2[N], b2[N], ans[N];
	int cnt[N];
	inline bool cmp(int a, int b) {return a>b;}
	bool check(int k) {
		int pos=1;
		for (int i=1; i<=k; ++i) {
			for (; pos<=n; ++pos) {
				if (a2[i]>b2[pos]) {
					++pos;
					goto jump;
				}
			}
			if (pos>n) return 0;
			jump: ;
		}
		return 1;
	}
	void solve() {
		for (int i=1; i<=n; ++i) ++cnt[a[i]];
		memcpy(a2, a, sizeof(a));
		memcpy(b2, b, sizeof(b));
		sort(a2+1, a2+n+1, cmp); sort(b2+1, b2+n+1, cmp);
		int l=0, r=n, mid;
		while (l<=r) {
			mid=(l+r)>>1;
			if (check(mid)) l=mid+1;
			else r=mid-1;
		}
		mid=l-1;
		//cout<<"mid: "<<mid<<endl;
		for (int i=1; i<=mid; ++i) --cnt[a2[i]];
		int pos=1;
		for (int i=1; i<=mid; ++i) {
			for (; pos<=n; ++pos) {
				if (a2[i]>b2[pos]) {
					ans[pos]=a2[i];
					++pos;
					goto jump2;
				}
			}
			jump2: ;
		}
		pos=1;
		for (int i=N; ~i; --i) 
			while (cnt[i]) {
				while (ans[pos]) ++pos;
				ans[pos]=i;
				--cnt[i];
			}
		for (int i=1; i<=n; ++i) printf("%d ", ans[i]);
		printf("\n");
		exit(0);
	}
}

namespace task{
	multiset<int> s;
	int tl[N<<2], tr[N<<2], sa[N<<2], sb[N<<2], sum[N<<2], k;
	#define tl(p) tl[p]
	#define tr(p) tr[p]
	#define sa(p) sa[p]
	#define sb(p) sb[p]
	#define sum(p) sum[p]
	void pushup(int p) {
		//cout<<"pushup "<<p<<' '<<tl(p)<<' '<<tr(p)<<endl;
		//cout<<"sa: "<<sa(p<<1)<<' '<<sa(p<<1|1)<<endl;
		//cout<<"sb: "<<sb(p<<1)<<' '<<sb(p<<1|1)<<endl;
		int dlt=min(sb(p<<1), sa(p<<1|1));
		sum(p)=sum(p<<1)+sum(p<<1|1)+dlt;
		sa(p)=sa(p<<1)+sa(p<<1|1)-dlt;
		sb(p)=sb(p<<1)+sb(p<<1|1)-dlt;
	}
	void build(int p, int l, int r) {
		tl(p)=l; tr(p)=r;
		if (l==r) return ;
		int mid=(l+r)>>1;
		build(p<<1, l, mid);
		build(p<<1|1, mid+1, r);
	}
	void upd(int p, int pos, int dat, int typ) {
		if (tl(p)==tr(p)) {(typ?sa:sb)[p]+=dat; return ;}
		int mid=(tl(p)+tr(p))>>1;
		if (pos<=mid) upd(p<<1, pos, dat, typ);
		else upd(p<<1|1, pos, dat, typ);
		pushup(p);
	}
	void query(int p) {
		cout<<"query "<<p<<' '<<tl(p)<<' '<<tr(p)<<' '<<sa(p)<<' '<<sb(p)<<' '<<sum(p)<<endl;
		if (tl(p)==tr(p)) return ;
		query(p<<1); query(p<<1|1);
	}
	void solve() {
		build(1, 1, N);
		for (int i=1; i<=n; ++i) upd(1, a[i], 1, 1), s.insert(a[i]);
		for (int i=1; i<=n; ++i) upd(1, b[i], 1, 0);
		k=sum(1);
		//query(1);
		//cout<<"k: "<<k<<endl;
		for (int i=1,l,r,mid,rb; i<=n; ++i) {
			upd(1, b[i], -1, 0);
			l=b[i]+1, r=rb=*s.rbegin();
			//cout<<"lr: "<<l<<' '<<r<<endl;
			while (l<=r) {
				mid=(l+r)>>1;
				upd(1, mid, -1, 1);
				if (sum(1)==k-1) l=mid+1;
				else r=mid-1;
				upd(1, mid, 1, 1);
			}
			if (b[i]+1<=rb) upd(1, l-1, -1, 1);
			if (b[i]+1<=rb && sum(1)==k-1) {--k; printf("%d ", l-1); s.erase(s.find(l-1));}
			else {
				if (b[i]+1<=rb) upd(1, l-1, 1, 1);
				l=1, r=b[i];
				while (l<=r) {
					mid=(l+r)>>1;
					upd(1, mid, -1, 1);
					if (sum(1)==k) l=mid+1;
					else r=mid-1;
					upd(1, mid, 1, 1);
				}
				upd(1, l-1, -1, 1);
				printf("%d ", l-1);
				//if (s.find(l-1)==s.end()) cout<<"error2"<<endl;
				s.erase(s.find(l-1));
			}
		}
		printf("\n");
		exit(0);
	}
}

signed main()
{
	n=read();
	for (int i=1; i<=n; ++i) b[i]=read();
	for (int i=1; i<=n; ++i) a[i]=read();
	//force::solve();
	//task1::solve();
	task::solve();
	
	return 0;
}
上一篇:IOI2021 D1T2 keys


下一篇:TranslateMessage消息翻译和DispatchMessage消息分发