【CFR#655】F Omkar ans Modes
题目
交互题
给出长度为\(n\)的单调不减序列,一次询问可以询问\([l,r]\)中的众数\(x\)(如果多个数字出现次数相同则返回最小的数字)以及\(x\)出现的次数\(f\)
设\(n\)中不同数字的个数为\(k\),询问次数不得超过\(4k\) ,输出整个的序列
\(1 \le n \le 2\times 10^5 \ , \ 1 \le k \le 25000\)
题解
调用函数\(solve(1,n)\)
- \(query(1,n)\)得到$(x,f) $(1)
- 找到满足\(2^k \le f \lt 2^{k+1}\) 的\(k\) ,query查询\(1 \to n\)中下标为\(2^k\)的倍数的点(2)
- 查询值为\(x\)的可能有\(1\)或\(2\)(3)
- 如果只有一个位置值为\(x\),设为\(j\),那么\([j,j+2^k-1]\)和\([j-2^k+1,j]\) 中至少有一个众数为\(x\),同时询问这两个区间即可得到\(x\)的准确位置
- 如果有两个位置的值为\(x\),设为\(j_1\)和\(j_2\),那么\([j_1-2^k+1,j_2]\)和\([j_1,j_2+2^k-1]\) 的众数均为\(x\),询问任意一个区间即可得到\(x\)的准确位置
- 递归调用solve确定左右两边剩下的区间
复杂度分析
- \(solve\)会被准确地调用\(k\)次,因此(1)的复杂度为\(O(k)\)
- 如果每次将(2)的查询存储起来,对于一个数值,一定被query一次或者两次,并且query一次的在(3)中被query两次,query两次的在(3)中被query一次,那么(2)(3)的复杂度为\(O(3k)\)
- 故总的询问复杂度为\(O(4k)\),时间复杂度应为\(O(n log \ n)\)
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk make_pair
using namespace std;
const int N=400010;
int n,a[N],x,f,k,p1,p2;
pii query(int l,int r){
printf("? %d %d\n",l,r);
fflush(stdout);
scanf("%d %d",&x,&f);
return mk(x,f);
}
void solve(int l,int r){
pii q=query(l,r);
if(q.second==r-l+1){
for(int i=l;i<=r;++i)a[i]=q.first;
return ;
}
for(k=1;(k<<1)<=q.second;k<<=1);
int i=k,L,R;while(i<l)i+=k;
for(;i<=r;i+=k){
if(!a[i])a[i]=query(i,i).first;
if(a[i]==q.first){p1=i;break;}
}
p2=p1+k;
if(p2<=r&&!a[p2])a[p2]=query(p2,p2).first;
if(a[p1]==a[p2]){
pii p=query(max(l,p1-k+1),p2);
L=p2-p.second+1,R=p2+q.second-p.second;
}else {
// k>>=1;
pii p=query(max(l,p1-k+1),p1);
if(p.first==q.first)
L=p1-p.second+1,R=p1+q.second-p.second;
else {
p=query(p1,min(p1+k,r));
R=p1+p.second-1,L=p1-q.second+p.second;
}
}
for(int j=L;j<=R;++j)a[j]=q.first;
if(l<L)solve(l,L-1);
if(R<r)solve(R+1,r);
}
int main(){
// freopen("F.in","r",stdin);
// freopen("F.out","w",stdout);
scanf("%d",&n);
solve(1,n);
printf("! ");for(int i=1;i<=n;++i)printf("%d ",a[i]);
fflush(stdout);
return 0;
}//tkys_Austin;