Codeforces Round #720 (Div. 2) C. Nastia and a Hidden Permutation 交互

传送门

文章目录

题意:

给你一个序列 p p p长度 n n n,每次可以执行两个种询问:
t = 1    m a x ( m i n ( x , p i ) , m i n ( x + 1 , p j ) ) t=1\ \ max(min(x,p_i),min(x+1,p_j)) t=1  max(min(x,pi),min(x+1,pj))
t = 2    m i n ( m a x ( x , p i ) , m a x ( x + 1 , p j ) ) t=2\ \ min(max(x,p_i),max(x+1,p_j)) t=2  min(max(x,pi),max(x+1,pj))
询问操作不能超过 ⌊ 3 ∗ n 2 ⌋ + 30 \left \lfloor \frac{3*n}{2} \right \rfloor+30 23n+30次。
n < = 1 e 4 n<=1e4 n<=1e4

思路:

交互题看到操作不能超过 ⌊ 3 ∗ n 2 ⌋ + 30 \left \lfloor \frac{3*n}{2} \right \rfloor+30 23n+30的时候容易想到通过 ⌊ n 2 ⌋ \left \lfloor \frac{n}{2} \right \rfloor 2n次操作询问出某个值,让后再通过 n − 1 n-1 n1次操作询问出所有值,下面考虑如何询问某个值。
其实 1 , 2 1,2 1,2操作是差不多的,我们这里利用第一个操作询问出来 n n n的位置。从 1 1 1开始,让 x = n − 1 x=n-1 x=n1,每次询问相邻两项,如果返回值为 n n n的话,说明 i + 1 i+1 i+1的位置是 n n n,如果是 n − 1 n-1 n1的话,将 i , i + 1 i,i+1 i,i+1的位置调换一下再询问一次看是否为 n n n。当 n n n为奇数的时候,如果最后都没找到 n n n的位置,那么说明 n n n的位置在最后一位。
现在知道 n n n的位置为 p o s pos pos,我们可以通过第二个操作询问出来所有值,我们让 p j = p o s , x = 1 p_j=pos,x=1 pj=pos,x=1,让后让 p i p_i pi为当前询问的位置,返回值即为当前位置的值。
这样问题就完美解决啦,总询问次数不会超过 ⌊ n 2 ⌋ + n + 1 \left \lfloor \frac{n}{2} \right \rfloor+n+1 2n+n+1

//#pragma GCC optimize("Ofast,no-stack-protector,unroll-loops,fast-math")
//#pragma GCC target("sse,sse2,sse3,ssse3,sse4.1,sse4.2,avx,avx2,popcnt,tune=native")
//#pragma GCC optimize(2)
#include<cstdio>
#include<iostream>
#include<string>
#include<cstring>
#include<map>
#include<cmath>
#include<cctype>
#include<vector>
#include<set>
#include<queue>
#include<algorithm>
#include<sstream>
#include<ctime>
#include<cstdlib>
#include<assert.h>
#define X first
#define Y second
#define L (u<<1)
#define R (u<<1|1)
#define pb push_back
#define mk make_pair
#define Mid (tr[u].l+tr[u].r>>1)
#define Len(u) (tr[u].r-tr[u].l+1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
#define db puts("---")
using namespace std;

//void rd_cre() { freopen("d://dp//data.txt","w",stdout); srand(time(NULL)); }
//void rd_ac() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//AC.txt","w",stdout); }
//void rd_wa() { freopen("d://dp//data.txt","r",stdin); freopen("d://dp//WA.txt","w",stdout); }

typedef long long LL;
typedef unsigned long long ULL;
typedef pair<int,int> PII;

const int N=1000010,mod=1e9+7,INF=0x3f3f3f3f;
const double eps=1e-6;

int n;
int a[N];

int query(int op,int i,int j,int x) {
	printf("? %d %d %d %d\n",op,i,j,x); fflush(stdout);
	int ans; scanf("%d",&ans);
	return ans;
}

int main()
{
//	ios::sync_with_stdio(false);
//	cin.tie(0);

	int _; scanf("%d",&_);
	while(_--) {
		scanf("%d",&n);
		int pos=-1;
		for(int i=1;i<=n-1;i+=2) {
			int now=query(1,i,i+1,n-1);
			if(now==n) {
				pos=i+1;
				break;
			}
			else if(now==n-1) {
				now=query(1,i+1,i,n-1);
				if(now==n) {
					pos=i;
					break;
				}
			}
		}
		if(pos==-1) pos=n;
		a[pos]=n;
		for(int i=1;i<=n;i++) {
			if(i==pos) continue;
			int now=query(2,i,pos,1);
			a[i]=now;
		}
		printf("! "); fflush(stdout);
		for(int i=1;i<=n;i++) printf("%d ",a[i]),assert(a[i]<=n),fflush(stdout);
		puts(""); fflush(stdout);
	}

	return 0;
}
/*

*/


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值