1 条题解
-
0
自动搬运
来自洛谷,原作者为

封禁用户
None搬运于
2025-08-24 23:00:53,当前版本为作者最后更新于2024-11-17 00:21:29,作者可能在搬运后再次修改,您可在原文处查看最新版自动搬运只会搬运当前题目点赞数最高的题解,您可前往洛谷题解查看更多
以下是正文
设 。给定一个 的排列 ,你在每次交互中询问一个区间 ,可以得到 中逆序对的数量对 取模的值。请在若干次交互后确定排列 。
交互次数上限:( 分),( 分)。
双倍经验:P9721。
推销相关文章!很明显,如果我们可以在可接受的询问次数内比较任意两项 的值(即实现一个
cmp函数),那么这不就是就是一个赤裸裸的 AT_practice_2 Subtask #2(交互式排序)吗!实际上,这个
cmp函数可以用 次询问实现:设 表示 是否是逆序对:若 ,则 ,否则 。
设 是 中逆序对的数量。对每个逆序对 分 种情况讨论:
- 情况下逆序对的个数:;
- 情况下逆序对的个数:;
- 情况下逆序对的个数:;
- 情况下逆序对的个数:。
由定义得 ,然后整理上面 个式子:
通过询问
? i j,我们可以得到 的值。因此,我们可以在 次询问内得到 的值(实际上就是 的值,因为 只能取 或 )。综上,我们用 次询问实现了
cmp函数。套上 的排序(比如归并排序)模板后,我们得到了一个总共需要 次询问的 解法,刚好能过!
(看来出题人在询问次数上卡了常数,差评!)特别提醒:这题卡了 STL
sort,用它只能得到 分!使用了 次询问的 AC Code(实际上它是 的):
#include <bits/stdc++.h> using namespace std; int a[1012],b[1012]; map<pair<int,int>,bool> p; bool check(int x,int y) { if(p.count(make_pair(x,y))) return p[make_pair(x,y)]; // 若之前进行过完全相同的询问,直接调用之前的询问结果 cout<<"? "<<x<<' '<<y<<endl; bool sw; cin>>sw; p[make_pair(x,y)]=sw; return sw; } bool cmp(int x,int y) { bool sw=true; if(x>y) sw=false; if(x>y) swap(x,y); sw^=check(x,y); if(y-x>1) sw^=check(x,y-1); if(y-x>1) sw^=check(x+1,y); if(y-x>2) sw^=check(x+1,y-1); // x = y 时答案显然是 0,就不用询问了 return sw; } void st(int l,int r) { // 归并排序 if(l==r) return; int lmid=(l+r)/2,rmid=lmid+1; st(l,lmid),st(rmid,r); int tmp[1012]={0}; int p1=l,p2=rmid,p3=l; while(p1!=rmid&&p2!=r+1) { if(cmp(a[p1],a[p2])) tmp[p3]=a[p1],p1++,p3++; else tmp[p3]=a[p2],p2++,p3++; } while(p1<rmid) tmp[p3]=a[p1],p1++,p3++; while(p2<r+1) tmp[p3]=a[p2],p2++,p3++; for(int i=l;i<=r;i++) a[i]=tmp[i]; } int main() { for(int i=1;i<=1000;i++) a[i]=i; st(1,1000); cout<<"! "; for(int i=1;i<=1000;i++) b[a[i]]=i; // 排序得到的结果是“1, 2, 3, ... 分别出现在哪个位置”,因此需要转换 for(int i=1;i<=1000;i++) cout<<b[i]<<' '; cout<<endl; return 0; }
- 1
信息
- ID
- 10524
- 时间
- 4000ms
- 内存
- 1024MiB
- 难度
- 5
- 标签
- 递交数
- 0
- 已通过
- 0
- 上传者