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;
}
posted @ 2021-03-27 21:26  mizu164  阅读(165)  评论(0编辑  收藏  举报