MX 暑假集训 7.12

【25時、ナイトコードで。×鏡音リン】限りなく灰色へ(向着无尽之灰)

太棒了学了好久的 dp 优化结果发现写出 \(n^2\) 式子只剩 \(20\) 分钟了,根本来不及优化。

[APIO2016] 最大差分

题意

有一个隐藏的长度为 \(n\) 的数组 \(a\) 满足 \(0\le a_1< a_2<a_3<\dots <a_n\le 10^{18}\),你每次可以给定 \([s,t]\) 并询问 \(a_i\in [s,t]\)\(a_i\) 的最大与最小值,你需要找出 \(a_{i+1}-a_{i}(1\le i\le n-1)\) 的最大值,有以下两个子任务。

  • \(\left\lfloor \frac{n+1}{2} \right\rfloor\) 次询问内求出最大值;

  • 令每次出现在 \([s,t]\) 中的 \(a_i\) 个数为 \(k\) 个,你总共询问了 \(m\) 次,则每次询问的 \(k\) 之和加上 \(m\) 不超过 \(3n\),即 \(\sum(k+1)\le 3n\)

solution

子任务 \(1\) 是简单的,每次询问值域并记录最大最小值,再缩减值域至最大最小值之间即可。

子任务 \(2\) 这个限制有些严,肯定不是分治状物了,考虑一下分块。

对值域做分块尝试缩减成子问题是不可取的,因为根本缩减不过来,而且扫一遍值域的复杂度太高了。

考虑从答案入手,如果我们知道了 \(a_1\)\(a_n\) 的值,那么答案不小于 \(\left\lceil \frac{a_n-a_1}{n-1}\right\rceil\),令这个值为 \(B\),对值域分块,那么块内不可能有答案,考虑跨块的答案即可。

找到 \(a_1,a_n\)\(n+1\) 的代价,块内是 \(n-1\) 次询问,问到了 \(n-2\) 个数,所以总代价不超过 \(3n-2\),可以通过。

Code
#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)

extern "C" void MinMax(long long,long long,long long*,long long*);
extern "C" long long findGap(int,int);

long long findGap(int T,int n) {
	if (T==1) {
		vector<ll> v1,v2,tmp;
		ll mn=-1,mx=1000000000000000000LL+1;
		int Tim=(n+1)/2;
		while (Tim--) {
			MinMax(mn+1,mx-1,&mn,&mx);
			v1.push_back(mn);
			v2.push_back(mx);
		}
		reverse(v2.begin(),v2.end());
		for (ll x:v1) tmp.push_back(x);
		for (ll x:v2) tmp.push_back(x);
		ll lst=-1,ret=0;
		for (ll x:tmp) {
			if (lst!=-1) ret=max(ret,x-lst);
			lst=x;
		}
		return ret;
	}
	ll mx=0,mn=0;
	MinMax(0,1000000000000000000LL,&mn,&mx);
	ll ret=0,B=(mx-mn)/(n-1)+((mx-mn)%(n-1)!=0),lst=mn;
	for (ll L=mn+1;L<mx;L+=B) {
		ll nL=0,nR=0;
		MinMax(L,min(L+B-1,mx-1),&nL,&nR);
		if (nL!=-1) {
			ret=max(ret,nL-lst);
			lst=nR;
		}
	}
	ret=max(ret,mx-lst);
	return ret;
}

[JOISC 2016] 神经衰弱 / Memory2

题意

\(n\) 种卡牌,每种各两张,将卡牌全部摆在桌上形成长度为 \(2n\) 的序列 \(a\),初始时,你并不知道 \(a\) 中的数是什么。

你可以进行如下操作。

  • 指定两个位置 \(i,j\),若 \(P_{a_i}>P_{a_j}\) 则返回 \(a_i\),否则返回 \(a_j\),其中 \(P\) 是一个内置的确定的权重排列。

你需要在 \(k\) 次操作内确定序列 \(a\)

\(1\le n\le 50,k=300\)

solution

不会做,只会 \(P_i=i\) 的特殊性质。

我们发现,确定一个位置 \(x\),将其与其他所有位置都比较一遍,记录所有返回值的次数,一定会有且仅有一个返回值出现奇数次,这个数就是 \(x\) 位置的值,原因是其他的数成对出现,贡献一定为 \(0\)\(2\)

每次随机选择 \(x\),将其与所有未确定位置询问一遍,若一个位置与其询问的结果不为位置 \(x\) 上的值,那么可以确定这个位置。

期望次数为 \(T(n)=n+T(\frac{n}{2})=2n\),精细实现可以轻松通过。

\(300\) 次和 \(250\) 次的确定性做法,但是不会喵。

Code
#include<cstdio>
#include<algorithm>
#include<vector>
#include<random>
#include "Memory2_lib.h"
using namespace std;
#define ll long long
#define qwq Ff472130
#define f(i,l,r) for (int i=l;i<=r;i++)
#define F(i,l,r) for (int i=l;i>=r;i--)

mt19937 Ff(8566);
inline int rnd(int l,int r) {
	ll k=Ff();
	return l+k%(r-l+1);
}

inline int ask(int x,int y,vector<vector<int>> &mp) {
	if (mp[x][y]!=-1) return mp[x][y];
	return mp[x][y]=mp[y][x]=Flip(x,y);
}

void Solve(int T,int n) {
	if (n==1) return Answer(0,1,0);
	vector<vector<int>> mp;
	f(i,1,n*2) {
		vector<int> init;
		f(j,1,n*2) init.push_back(-1);
		mp.push_back(init);
	}
	vector<int> id,t(n*2),col[60],vis(n*2);
	f(i,0,n*2-1) id.push_back(i);
	while (1) {
		if (id.size()==2) {
			col[ask(id[0],id[1],mp)].push_back(id[0]);
			col[ask(id[0],id[1],mp)].push_back(id[1]);
			break;
		}
		int k=id[rnd(0,id.size()-1)],val=-1;
		if (vis[k]) continue;
		if (id.size()>20) {
			vector<int> chk;
			int pos=rnd(0,id.size()-1);
			while (pos==k) pos=rnd(0,id.size()-1);
			chk.push_back(ask(k,pos,mp));
			pos=rnd(0,id.size()-1);
			while (pos==k) pos=rnd(0,id.size()-1);
			chk.push_back(ask(k,pos,mp));
			pos=rnd(0,id.size()-1);
			while (pos==k) pos=rnd(0,id.size()-1);
			chk.push_back(ask(k,pos,mp));
			pos=rnd(0,id.size()-1);
			while (pos==k) pos=rnd(0,id.size()-1);
			chk.push_back(ask(k,pos,mp));
			pos=rnd(0,id.size()-1);
			while (pos==k) pos=rnd(0,id.size()-1);
			chk.push_back(ask(k,pos,mp));
			sort(chk.begin(),chk.end());
			if (chk[0]==chk[1]&&chk[1]==chk[2]) continue;
			if (chk[1]==chk[2]&&chk[2]==chk[3]) continue;
			if (chk[2]==chk[3]&&chk[3]==chk[4]) continue;
		}
		vis[k]=1;
		for (int x:id) if (x^k) t[ask(k,x,mp)]^=1;
		f(i,0,n-1) if (t[i]) val=i,t[i]=0;
		vector<int> nxt;
		for (int x:id) {
			if (x!=k&&ask(k,x,mp)!=val) col[ask(k,x,mp)].push_back(x);
			else nxt.push_back(x);
		}
		id=nxt;
	}
	f(i,0,n-1) Answer(col[i][0],col[i][1],i);
}
posted @ 2026-07-12 21:01  Ff472130  阅读(5)  评论(0)    收藏  举报