CF1090F - How to Learn You Score(构造)

CF1090F - How to Learn You Score

Solution

很不戳的构造题。

首先观察数据范围: n ∈ [ 5 , 1000 ] n\in[5,1000] n[5,1000],这启发我们什么? n = 5 n=5 n=5的时候解是唯一的,因此我们可以把 n n n切成若干段长度为 5 5 5的段,每一段分别求答案,最后拼起来。

然后我们考虑长度为 5 5 5的段,显然 4 n 4n 4n的询问上界允许我们把十种三元组都询问出来。

我们先确定这些数的值,而不考虑位置,设这五个值从小到大依次为 a 1 . . . a 5 a_1...a_5 a1...a5,十种三元组: 123 , 124 , 134 , 125 , 135 , 145 , 234 , 235 , 245 , 345 123,124,134,125,135,145,234,235,245,345 123,124,134,125,135,145,234,235,245,345

因此十种三元组的值为 13 , 14 , 14 , 15 , 15 , 15 , 24 , 25 , 25 , 35 13,14,14,15,15,15,24,25,25,35 13,14,14,15,15,15,24,25,25,35,且其中的排序关系基本可以知道了,为 13 < 14 < 15    ?    24 < 25 < 35 13<14<15\;?\;24<25<35 13<14<15?24<25<35,到这里我们已经可以通过询问值的加减求出每个数的值了(详见 C o d e Code Code)。

接下来就是确定每个值的位置,因为我们已经证明 5 5 5的时候解唯一,因此直接 5 ! 5! 5!枚举全排列即可。

时间复杂度 O ( n ) O(n) O(n),询问次数 2 n 2n 2n

Code

#include <bits/stdc++.h>
 
using namespace std;
 
template<typename T> inline bool upmin(T &x, T y) { return y < x ? x = y, 1 : 0; }
template<typename T> inline bool upmax(T &x, T y) { return x < y ? x = y, 1 : 0; }
 
#define MP(A,B) make_pair(A,B)
#define PB(A) push_back(A)
#define SIZE(A) ((int)A.size())
#define LEN(A) ((int)A.length())
#define FOR(i,a,b) for(int i=(a);i<(b);++i)
#define fi first
#define se second
 
typedef long long ll;
typedef unsigned long long ull;
typedef long double lod;
typedef pair<int, int> PR;
typedef vector<int> VI; 
 
const lod eps = 1e-9;
const lod pi = acos(-1);
const int oo = 1 << 30;
const ll loo = 1ll << 60;
const int mods = 998244353;
const int MAXN = 600005;
const int INF = 0x3f3f3f3f; //1061109567
/*--------------------------------------------------------------------*/

#define int ll


struct Anode{ int x, y, z, s; } A[20];
int Ans[MAXN], n;

int min(int x, int y, int z) { return min(x, min(y, z)); }
int max(int x, int y, int z) { return max(x, max(y, z)); }
void solve(int l, int r) {
	int num = 0;
	for (int i = l; i <= r ; ++ i)
		for (int j = i + 1; j <= r ; ++ j)
			for (int k = j + 1; k <= r ; ++ k) {
				cout << "? " << i << " " << j << " " << k << endl;
				cin >> A[++ num].s;
				A[num].x = i, A[num].y = j, A[num].z = k;
			}
	sort(A + 1, A + num + 1, [&](Anode a, Anode b) { return a.s < b.s; });
	int s13 = A[1].s;
	int s25 = A[9].s;
	int s35 = A[10].s;
	int s15 = (A[4].s == A[5].s ? A[4].s : (A[5].s == A[6].s ? A[5].s : A[4].s));
	int s24 = A[4].s + A[5].s + A[6].s + A[7].s - s15 * 3;
	int s135 = (s13 + s15 + s35) / 2;
	
	Ans[l] = s135 - s35;
	Ans[l + 2] = s135 - s15;
	Ans[l + 4] = s135 - s13;
	Ans[l + 1] = s25 - Ans[l + 4];
	Ans[l + 3] = s24 - Ans[l + 1];
	sort(Ans + l, Ans + l + 5);
	
	for (;;) {
		int flag = 1;
		for (int i = 1; i <= num ; ++ i)
			if (min(Ans[A[i].x], Ans[A[i].y], Ans[A[i].z]) + max(Ans[A[i].x], Ans[A[i].y], Ans[A[i].z]) != A[i].s) { flag = 0; break; }
		if (flag) return;
		next_permutation(Ans + l, Ans + l + 5);
	}
}
signed main() {
	cin >> n;
	for (int i = 1; i + 4 < n ; i += 5) solve(i, i + 4);
	solve(n - 4, n);
	cout << "! ";
	for (int i = 1; i <= n ; ++ i) cout << Ans[i] << " "; cout << endl;
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值