CF 1584D. Guess the Permutation(交互题)

题目链接


题意:

交互题。
有一段长度为 n 的原始排列数组 (a[i] = i) 被 i,j,k 三个数字控制所翻转:
区间 [i,j-1] 翻转,区间 [j,k] 翻转。(区间翻转:首位置和末位置互换,次位置和末次位置互换…)
对系统最多可以有40次询问,每次询问输入 l,r,返回的是 [l,r] 这段区间中的逆序对数。
求处理这段数组所用的 i,j,k

前言:

第一次做交互题,先搞清楚玩法:
可以向系统提出一定次询问,每次系统会给出问题的答案。
需要根据这些答案,来推算出题目的答案。

一般和二进制,二分相关。

询问模板:

// 问系统params, 返回系统给你的答案
T ask(T params ...) {
	cout << params << endl; // 输出你要问的问题 给系统
    cout.flush(); // 清空缓存
    cin >> ans;
    return ans;
}

然后不要开 Ios,否则会有 Idleness limit exceeded


回到这道题:

思路:

举个例子:

1 2 3 4 5 6 7 8 9 10 11
	————— ——————————
1 2 5 4 3 10 9 8 7 6 11
	————— ——————————

将区间 [3,5], [6,10] 翻转,对应的 i = 3, j = 6, k = 10。

现在要经过不超过40次询问来得到这三个数,每次询问得到询问区间中的逆序对数。

因为数组初始为原始排列,所以初始是没有逆序对数的。
所以可以二分询问 [1,mid] 这段区间的逆序对数,如果不为0,说明 mid 点在翻转区间中,即在点 i 右边。如此,便可以得到第一个数 i。区间长度在 int 范围内,所以最多询问32次。

接下来询问 [i,n][i+1,n] 区间中的逆序对数。
如例,[i,n]中的逆序对数也为 [3,5] 中的逆序对数,即 2+1,[i+1,n]的逆序对数也为[4,5] 中的逆序对数,即1。那么,两个区间逆序对数之差+1便为第一段翻转区间的长度。得到第二个数 j。

类似,询问 [j,n][j+1,n] 区间中的逆序对数。
如例,[j,n] 中逆序对数 4+3+2+1,[j+1,n]中逆序对数 3+2+1,两者之差便是第二段区间的长度。遂得到第三个数 k。

原理:因为翻转过的区间为递减区间,所以一个位置到末尾的逆序对数为下一位置到末尾的逆序对数+区间长度-1。

一共询问 32+2+2=36 次。

Code:

const int N = 100010, mod = 1e9+7;
int T, n, m;
int a[N];

ll ask(int l,int r)
{
	cout<<"? "<<l<<" "<<r<<"\n";
	cout.flush();
	ll ans; cin>>ans;
	return ans;
}

bool check(int mid)
{
	if(ask(1,mid)) return 1;
	return 0;
}

int main(){
	cin>>T;
	while(T--)
	{
		cin>>n;
		
		int l=1,r=n;
		while(l<r)
		{
			int mid=l+r>>1;
			if(check(mid)) r=mid;
			else l=mid+1;
		}
		
		int i = l-1;
		int j = i+ask(i,n)-ask(i+1,n)+1;
		int k = j+ask(j,n)-ask(j+1,n);
		
		cout<<"! "<<i<<" "<<j<<" "<<k<<"\n";
	}
	
	return 0;
}

第一次做交互题,还不错。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值