题意
题目链接。
交互题。
狐狸给你两个正整数 $n,k$,并且她有一个长度为 $n$ 的隐藏数组。对于一段区间 $(l,r)$,其价值 $f(l,r)$ 等于长度乘以区间最大值。
你有 $2n$ 次查询次数,每次查询形如 ? l x
,狐狸会告诉你一个数 $r$,表示最小的满足 $f(l,r)=x$ 的 $r$,或是 $n+1$ 表示不存在 $r$。
你的目标是找到最大的 $m$,使得原数组能够被分割成 $k$ 个子数组,且每个子数组的价值都为 $m$。
解析
爱做交互题。
根据对范围 $2n$ 的猜测,容易想到可以将查询分成两块查询次数分别不多于 $n$ 次的查询。发现数组的最大值是至关重要的,因而我们可以通过至多 $n$ 次形如 ? 1 n*i
的查询($i$ 从 $n$ 到 $1$)判断数组的最大值是多少。我们发现只有当数组的最大值为 $i$ 时,上述询问才会返回一个唯一的 $r=n$。
找到最大值后,不妨考虑 $m$ 的可能值。由于在这 $k$ 个区间后,必定有一个区间包含最大值(记作 $max$),且这个区间的长度一定不会超过 $\lfloor \frac{n}{k} \rfloor$。这是因为如果这个包含最大值的区间长度比 $\lfloor \frac{n}{k} \rfloor$ 要大,那么这 $k$ 的区间的长度和一定会超过 $n$。因此,我们可以枚举 $m$ 的可能性,即从 $\max \times \lfloor \frac{n}{k} \rfloor$ 到 $\max \times 1$。
对于每个可能的 $m$,我们可以这样判断其合法性:先询问 ? 1 m
,然后若返回 $n+1$,显然不合法,否则若返回 $r$,再询问 ? r+1 m
即可。以此类推,我们可以通过至多 $k$ 次询问来判断这个 $m$ 是否合法。
具体的,在询问过程中,以下情况均为不合法:
- 任意一次询问返回了 $n+1$。
- 经过 $k$ 次询问后,数组仍然没有被全部覆盖。
- 还没有询问 $k$ 次,数组就被全覆盖了。
您可以通过查询代码中的 check
函数来理解详细的验证过程。
你发现这一部分的查询次数为 $\lfloor \frac{n}{k} \rfloor \times k$,不会超过 $n$。故问题解决。
代码
#include<bits/stdc++.h>
#define ll long long
#define int long long
#define pi pair<int,int>
#define fi first
#define se second
#define mk make_pair
#define pb push_back
using namespace std;
int n,k,maxx;
bool check(int len){
int now=1,nowk=0;
while(now<=n&&nowk<k){
printf("? %lld %lldn",now,maxx*len);
fflush(stdout);
int tmp;
scanf("%lld",&tmp);
if(tmp==n+1) return false;
now=tmp+1;nowk++;
}
if(now>n&&nowk==k) return true;
return false;
}
void solve(){
scanf("%lld %lld",&n,&k);
for(int i=n;i>=1;i--){
printf("? 1 %lldn",i*n);
fflush(stdout);
int tmp;
scanf("%lld",&tmp);
if(tmp!=n+1){
maxx=i;break;
}
}
for(int i=n/k;i>=1;i--){
if(check(i)){
printf("! %lldn",i*maxx);
fflush(stdout);
int tmp;
scanf("%lld",&tmp);
if(tmp==-1) exit(0);
return;
}
}
printf("! -1n");
fflush(stdout);
int tmp;
scanf("%lld",&tmp);
if(tmp==-1) exit(0);
return;
}
signed main(){
int T;
scanf("%lld",&T);
while(T--) solve();
return 0;
}
根据博主的思路写得,过了!
这是我写得第一道交互题!
(☆ω☆)
代码如下:
https://codeforces.com/contest/1973/submission/261541299
好耶!继续加油哇!