[KUPC2013 I] σ 题解
这种排列交互题可以使用随机化调整法,一起感受
随机
对于我们需要求出的
其中
先随机一个
记得特判一下
放代码:
#include<bits/stdc++.h>
using namespace std;
const int Q=240;
mt19937 g(time(0));
int main(){
ios::sync_with_stdio(false);
int n; cin>>n;
if(n==1)cout<<"! 1"<<endl,exit(0);
vector<int> r(n);
vector p(Q,vector<int>(n)),m=p,c=p;
iota(r.begin(),r.end(),0);
for(int i=0;i<Q;i++){
shuffle(r.begin(),r.end(),g),p[i]=r;
for(int j=0;j<n;j++)
m[i][p[i][j]]=j;
} // 生成若干询问排列,顺带求出 m 数组
for(int i=0;i<Q;i++){
cout<<"? ";
for(int j=0;j<n;j++)
cout<<p[i][j]+1<<' ';
cout<<endl;
for(int j=0;j<n;j++){
int x; cin>>x,c[i][x]++;
}
} // 进行询问,求出 c 数组
int P=Q*n;
for(int i=0;i<Q;i++)
for(int j=0;j<n;j++){
int d=abs(m[i][r[j]]-j);
if(--c[i][d]>=0)P--;
} // 求出初始的 P
uniform_int_distribution<> u(0,n-1);
auto op=[&](int a,int b){
int P=0,w=0;
// 分别为分数的减量(与增量相对)和还原标记
for(int i=0;i<Q;i++){
if(c[i][abs(m[i][r[a]]-a)]++>=0)P--;
if(c[i][abs(m[i][r[b]]-b)]++>=0)P--;
if(--c[i][abs(m[i][r[a]]-b)]>=0)P++;
if(--c[i][abs(m[i][r[b]]-a)]>=0)P++;
if(w=i;P<-9)break;
// 小优化,不让答案在一个前缀增量太大
} // O(Q) 计算修改后的 c 数组
if(P<0){
for(int i=0;i<=w;i++){
c[i][abs(m[i][r[a]]-a)]--;
c[i][abs(m[i][r[b]]-b)]--;
c[i][abs(m[i][r[a]]-b)]++;
c[i][abs(m[i][r[b]]-a)]++;
} // 还原 c 数组
return P;
} // 会让答案变大,不采纳
return swap(r[a],r[b]),P;
};
while(P){
int x=u(g),y=u(g);
while(x==y)y=u(g);
if(int p=op(x,y);p>0)P-=p;
} // 循环直到找到答案
cout<<"! ";
for(int i:r)cout<<i+1<<' ';
cout<<endl;
return 0;
}