UOJ153【UR #10】世界线【交互、构造】
这是一道交互题
给定正整数 \(n\),交互器有长为 \(n\) 的排列 \(A\),你需要实现以下函数求出 \(A\):
- \(\texttt{int query_permutation(int n, int ans[])}\):
- \(n\) 表示排列长度。
- \(ans\) 是一个
int
数组,若能求出答案,你需要将 \(A_i\) 存到 \(ans[i]\) 作为结果并返回 \(1\),否则返回 \(0\)。
你可以调用以下函数来与交互器进行交互:
- \(\texttt{void new_round()}\):
- 调用这个函数后,开启一轮新的实验的第 \(1\) 阶段,交互器生成 \(2n\) 个点的无向图,初始有 \(n\) 条边,分别为 \((i,A_i+n)\)。
- 你至多能调用这个函数 \(2\) 次。
- \(\texttt{void next_step()}\):
- 调用这个函数后,进入实验的下一阶段。
- 你只能在实验的第 \(1\) 阶段调用这个函数。
- \(\texttt{void addedge(int u, int v)}\):
- 你需要保证 \(u,v\in[1,n]\)。
- 交互器在图上连一条边 \((u,v)\)。
- 你只能在实验的第 \(1\) 阶段调用这个函数。
- 对于每一组数据,你至多能调用这个函数 \(2(n-1)\) 次。
- \(\texttt{int query(int u, int v)}\):
- 你需要保证 \(u,v\in[1,n]\)。
- 若 \(u+n\) 与 \(v+n\) 在图上连通,则返回 \(1\)。否则返回 \(0\)。
- 你只能在实验的第 \(2\) 阶段调用这个函数。
- 对于每一组数据,你至多能调用这个函数 \(2\cdot 10^6\) 次。
\(T\) 组数据。\(T\le 10,n\le 10^4\)。
首先无解当且仅当 \(n=2\),因为当 \(n>2\) 时可以搞一个暴力:第一次 \((1,2),(3,4),\cdots\),第二次 \((2,3),(4,5),\cdots\)。
然而这个需要查询 \(\texttt{query}\) \(O(n^2)\) 次,究其原因是连通块数量太多,每次查询一个点所在连通块大概需要 \(O(\)连通块数\()\) 次。因此我们要尽量减少连通块数。
于是可以得到一个想法:把点排成方阵,多余的单开一列,第一次查询行连通性,第二次查询列连通性。
这显然很不行,因为行、列之间调换下顺序你就看不出来了,因此我们需要能直接看出所在连通块的构造方案:
可以先把列孤立点 (B) 所在行求出来,之后每个点可以通过所在连通块大小确定。
\(\texttt{query}\) 次数 \(O(n\sqrt n)\),常数不会算,但是可过(
#include"worldline.h"
#include<bits/stdc++.h>
using namespace std;
const int N = 10003;
int m, k, rt[N], S[N], c[2][N], s[2][N];
void work(int n, int c[N], int s[N]){
next_step(); k = 0;
for(int i = 1, j;i <= n;++ i){
for(j = 1;j <= k;++ j) if(query(i, rt[j])){c[i] = j; ++ s[j]; break;}
if(j > k){rt[++k] = i; c[i] = k; s[k] = 1;}
}
}
int query_permutation(int n, int ans[]){
if(n == 2) return 0;
if(n == 1){ans[1] = 1; return 1;}
for(m = 1;;++ m){S[m] = S[m-1] + m; if(S[m] >= n) break;}
S[m] = n; new_round();
for(int i = 1;i <= m;++ i)
for(int j = S[i-1]+1;j < S[i];++ j) addedge(j, S[i]);
work(n, c[0], s[0]); new_round();
for(int i = 1;i < m;++ i)
for(int j = i;j < m-1;++ j) addedge(S[j] + i, S[i]);
for(int i = 1;i < S[m] - S[m-1];++ i) addedge(n - i, S[m - i]);
work(n, c[1], s[1]);
if(S[m] - S[m-1] < m){ int u;
for(u = 1;u <= n;++ u)
if(s[0][c[0][u]] == S[m] - S[m-1] && s[1][c[1][u]] == 1) break;
for(int i = 1;i <= n;++ i) if(c[0][i] == c[0][u]){
ans[n - s[1][c[1][i]] + 1] = i; -- s[1][c[1][i]];
} -- m; s[0][c[0][u]] = 0;
} for(int i = 1;i <= n;++ i) if(s[0][c[0][i]])
ans[S[s[0][c[0][i]] - 1] + m - s[1][c[1][i]] + 1] = i;
return 1;
}