1584D - Guess the Permutation & 交互题入门
一、 交互题简介
第一次做交互题,用了大概半天时间学习了交互题的概念和大致做法
交互题跟一般题相反, 一般题目是系统给你输入数据, 让你输出答案, 系统判断答案是否正确
而交互题则类似于电视节目中的猜数字, 你可以理解为后台有一些数据, 但是需要你去询问, 系统会根据你的询问给你答案, 需要你在规定的询问次数内得到答案并输出
主要的思想是二分、三分、随机数,难点在于几乎无法本地自测
交互题是special judge
中的一种, 交互题的大概写法是:
// 问系统params, 返回系统给你的答案
T ask(T params ...) {
cout << params << endl; // 输出你要问的问题 给系统
cout.flush(); // 清空缓存
// System.out.flush() java
// fflush(stdout) C
// stdout.flush() py
cin >> ans;
return ans;
}
以1584D为例, 介绍下这道题怎么做
二、 题目
有一个长度为n的数组, n<1e9, 初始时是自然排列. 后台有3个参数 i , j , k i, j, k i,j,k, 对这个数组的 [ i , j − 1 ] [i,j-1] [i,j−1]和 [ j , k ] [j,k] [j,k]分别反转, 得到一个新数组
你可以对系统询问, 每次询问两个参数l, r, 系统回答你反转后的数组的a[l]…a[r]部分有多少个逆序对
要求在40次询问内, 把参数 i , j , k i, j, k i,j,k, 求出来
这道题的难度为2000分, 但其实只是难在题型上, 理解了交互题的基础知识以后这题的思路上真的很简单
首先我们看, 本题中的性质:
- 前i个数的逆序对的个数是单调递增的, 不会出现 i < j i<j i<j, a s k ( 1 , i ) > a s k ( 1 , j ) ask(1, i)>ask(1, j) ask(1,i)>ask(1,j)
- i i i之前是自然排列, 没有逆序对
- 如果我们找到了反转起点和反转终点, 那么逆序对的数量从右到左是0,1,2,3,… n-1
根据性质1, 我们二分的问, 问出第一个ask(1, i) > 0的, 就把参数i求出来了
然后问(i, n)和(i+1, n) 其差值+1就是第一段反转部分的长度 进而知道j
再问(j, n)和(j+1, n)就知道k
数组长度不超过int, 所以最多32+4=36次询问
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int t, n;
ll ask(int l, int r) {
cout << "? " << l << ' ' << r << endl;
cout.flush();
ll ans;
cin >> ans;
return ans;
}
int main() {
cin >> t;
while (t--) {
cin >> n;
int l = 1, r = n;
while (l < r) {
int mid = (l + r) >> 1;
if (ask(1, mid) > 0) r = mid;
else l = mid + 1;
}
int i = l - 1;
int j = ask(i, n) - ask(i + 1, n) + i + 1;
int k = ask(j, n) - ask(j + 1, n) + j;
cout << "! " << i << ' ' << j << ' ' << k << endl;
}
return 0;
}
// 5 4 3 2
// 6 3 2 1