[题解]CF1589D
题意:
sb交互题 。。。
有一个$n$个数的排列$\{a_i\}$,$n$为$10^9$级,初始$a_i=i$,(测试者)选出一个三元组 $(i,j,k)$,并对$[i,j-1][j,k]$分别倒序。
你可以询问区间$[l,r]$的逆序对数,至多不超过40次,推测并回答出三元组$(i,j,k)$。
分析以及思维过程:
首先看到数据范围为 $10^9$ 级别,询问次数不超过40,故考虑 $\log n $ 级别的算法。
为方便书写 $ask(l,r)$ 表示询问$[l,r]$的逆序对数。
第一反应:
考虑二分,首先 $ask(1,n)$ ,然后对于区间$[1,n]$查找左端点,如果$ask(mid,n)$不等于$ask(1,n)$那么左端点就在$[1,mid-1]$,因为逆序对减少了,反之则在$[mid+1,n]$
二分出左端点、右端点,即可求出中间点,区间长度为$a,b$那么$a\times (a-1)/2+b\times (b-1)/2=ask(1,n)$。
交上去才发现询问次数过多……
因为二分左端点和右端点的时间复杂度都是$\log_2 10^9$的,所以总查找次数会达到$30\times2=60$次
所以我们只能二分一次。
先二分左端点$i$,并推出$j,k$
利用好这个排列的特征 , 它本身单调递增,那么颠倒的部分就是单调递减的 。
如果有一个单调递减的序列
$\{x , x-1,x-2,……,1\}$
我们在此之前加一个数$x+1$ 那么逆序对数会增加$x$组$\{x+1,x , x-1,x-2,……,1\}$
利用这个性质,我们询问$ask(i,n),ask(i+1,n)$这之差就是$i$造成的逆序对数,既$j+1-i$
可以推出$j$
推出$j$后同理$ask(j,n),ask(j+1,n)$即可求出$k$
ACcode:
C++
#include<bits/stdc++.h>
#define int long long
using namespace std;
int a[102],b[102],n,t,l,r,zs,i,j,k;
bool check(int a,int b)
{
cout<<'?'<<' '<<a<<' '<<b<<endl;
cout.flush();
int p;
cin>>p;
if(p==zs) return 1;
return 0;
}
int asknxd(int l,int r)
{
cout<<'?'<<' '<<l<<' '<<r<<endl;
cout.flush();
int p;
cin>>p;
return p;
}
signed main()
{
cin>>t;
while(t--)
{
cin>>n;
cout<<'?'<<' '<<1<<' '<<n<<endl;
cout.flush();//因为这行痛失D题
cin>>zs;
l=1;r=n;
int ans = -1;
while(l<=r)
{
int mid = l+r>>1;
if(check(mid,n)) ans = mid ,l=mid + 1;
else r=mid-1;
}
i= ans ;
int nxd1 =asknxd(i,n);
int nxd2 =asknxd(i+1,n);
j=i+(nxd1-nxd2)+1;
nxd1 =asknxd(j,n) ;
nxd2 =asknxd(j+1,n) ;
k=j+(nxd1-nxd2);
cout<<'!'<<' '<<i<<' '<<j<<' '<<k<<endl;
cout.flush();
}
}
很不错的一道题,考完后才发现非常简单
本文已经结束了。本文作者:ღꦿ࿐(DeepSea),转载请注明原文链接:https://www.cnblogs.com/Dreamerkk/p/17970994,谢谢你的阅读或转载!

浙公网安备 33010602011771号