CF1584D Guess the Permutation 题解

文章介绍了CF1584D问题的解法,利用二分查找确定不相交区间的翻转次数,分析每段单调区间内逆序对数量,最终确定整个序列的逆序对分布。

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] 中逆序对的个数。

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

下一步确定 jjj 的值。我们可以询问 [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] 中逆序对的个数为 x1x_1x1[i+1,n][i+1,n][i+1,n] 中逆序对的个数为 x2x_2x2,则 x1−x2=(j−i)×(j−i−1)2−(j−i−1)×(j−i−2)2=j−i−1x_1 - x_2 = \dfrac{(j - i) \times (j - i - 1)}{2} - \dfrac{(j - i - 1) \times (j - i - 2)}{2} = j - i - 1x1x2=2(ji)×(ji1)2(ji1)×(ji2)=ji1。因为我们已经确定了 iii 的值,所以即可确定 jjj 的值。

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

在二分过程中,我们最多询问 ⌈log⁡2109⌉=30\lceil \log_2 10^9 \rceil = 30log2109=30 次确定 iii,再用 444 次确定 j,kj,kj,k。所以一定在 404040 次以内。

代码

#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、付费专栏及课程。

余额充值