CF1584D Guess the Permutation 题解

CF1584D Guess the Permutation 题解

被薄纱了。

解法

考虑何时产生逆序对。在翻转两个不相交的区间后,原序列被分为四段,分别是 [ 1 , i − 1 ] , [ i , j − 1 ] , [ j , k ] , [ k + 1 , n ] [1,i-1],[i,j-1],[j,k],[k+1,n] [1,i1],[i,j1],[j,k],[k+1,n]

每一段中数都是单调的,分别是单调递增,单调递减,单调递减,单调递增。只有中间两段内部有逆序对,个数分别是 ( j − i ) × ( j − i − 1 ) 2 , ( k − j + 1 ) × ( k − j ) 2 \dfrac{(j - i) \times (j - i - 1)}{2},\dfrac{(k - j + 1) \times (k - j)}{2} 2(ji)×(ji1),2(kj+1)×(kj)。我们询问 [ 1 , n ] [1,n] [1,n] 中逆序对的个数等价于询问 [ i , k ] [i,k] [i,k] 中逆序对的个数。

我们二分确定 i i i 的值,即每次询问 [ 1 , m i d ] [1,mid] [1,mid] 中逆序对的个数,如果没有逆序对则表示 m i d ≤ i − 1 mid \le i-1 midi1,否则表示 m i d ≥ i mid \ge i midi

下一步确定 j j j 的值。我们可以询问 [ i , n ] [i,n] [i,n] 中逆序对的个数,这等价于询问 [ i , k ] [i,k] [i,k] 中逆序对的个数。我们再询问 [ i + 1 , n ] [i+1,n] [i+1,n] 中逆序对的个数。根据上面的推导,设 [ i , n ] [i,n] [i,n] 中逆序对的个数为 x 1 x_1 x1 [ i + 1 , n ] [i+1,n] [i+1,n] 中逆序对的个数为 x 2 x_2 x2,则 x 1 − x 2 = ( j − i ) × ( j − i − 1 ) 2 − ( j − i − 1 ) × ( j − i − 2 ) 2 = j − i − 1 x_1 - x_2 = \dfrac{(j - i) \times (j - i - 1)}{2} - \dfrac{(j - i - 1) \times (j - i - 2)}{2} = j - i - 1 x1x2=2(ji)×(ji1)2(ji1)×(ji2)=ji1。因为我们已经确定了 i i i 的值,所以即可确定 j j j 的值。

类似于 j j j,我们可以确定 k k k 的值。我们可以询问 [ j , n ] [j,n] [j,n] 中逆序对的个数和 [ j + 1 , n ] [j+1,n] [j+1,n] 中逆序对的个数。设 [ j , n ] [j,n] [j,n] 中逆序对的个数为 x 1 x_1 x1 [ j + 1 , n ] [j+1,n] [j+1,n] 中逆序对的个数为 x 2 x_2 x2,则 x 1 − x 2 = ( k − j + 1 ) × ( k − j ) 2 − ( k − j ) × ( k − j − 1 ) 2 = k − j x_1 - x_2 = \dfrac{(k - j + 1) \times (k - j)}{2} - \dfrac{(k - j) \times (k - j - 1)}{2} = k - j x1x2=2(kj+1)×(kj)2(kj)×(kj1)=kj

在二分过程中,我们最多询问 ⌈ log ⁡ 2 1 0 9 ⌉ = 30 \lceil \log_2 10^9 \rceil = 30 log2109=30 次确定 i i i,再用 4 4 4 次确定 j , k j,k j,k。所以一定在 40 40 40 次以内。

代码

#include<bits/stdc++.h>
using namespace std;
#define int long long
int t,n,l,r,mid,tmp,i,j,k,xx1,xx2;
inline void ask(const int &l,const int &r) noexcept
{
	printf("? %d %d\n",l,r),fflush(stdout);
}
signed main()
{
	scanf("%lld",&t);
	while(t--)
	{
		scanf("%lld",&n);
		l=1,r=n;
		while(l<=r)
		{
			mid=(l+r)/2,ask(1,mid),scanf("%lld",&tmp);
			if(tmp==0) l=mid+1;
			else r=mid-1;
		}
		i=l-1;
		ask(i,n),ask(i+1,n),scanf("%lld %lld",&xx1,&xx2);
		j=xx1-xx2+i+1;
		ask(j,n),ask(j+1,n),scanf("%lld %lld",&xx1,&xx2);
		k=xx1-xx2+j;
		printf("! %lld %lld %lld\n",i,j,k),fflush(stdout);
	}
	return 0;
}
评论 14
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值