[ZZQTASK]GRA

题目

题目背景
在公元 9021 9021 9021 年,抽水马桶统一了全世界,开创了 人类 地球的新纪元。

丑话说在前面:这是一道交互题

题目描述(故事纯属虚构,不代表个人立场)
有一个抽水马桶(学名 s p l a s h    y o u r    a s s \rm splash\;your\;ass splashyourass,下文简称为 s y a \rm sya sya s y \rm sy sy)准备了八个压路机来修路(下简称为八个压路)。从空中俯瞰, s y \rm sy sy 的城市恰好是一个单位圆的 n n n 等分点。

为了让八个压路同时工作, s y \rm sy sy 决定修建的道路不能相交(除非是在某个城市处相交)。另外, s y \rm sy sy 只准备修建 n − 1 n-1 n1 条路,但是要让任意两个城市之间有路径连接。

糟糕的是, s y \rm sy sy 最喜欢棕色(因为与 💩 颜色相近),最讨厌红色(因为长臂 🐒 经常来吃霸王餐)。任意两个城市之间的道路都是棕色或者红色, s y \rm sy sy 决定,道路必须全部是棕色或者红色(所谓陷之死地而后生,置之亡地而后存)。

现在,你可以询问高傲的 s y \rm sy sy 最多 2 n 2n 2n 次道路的颜色,然后告诉 T a Ta Ta 一个合法的修建道路的方案。

数据范围与提示
n ≤ 1 0 3 n\le 10^3 n103 。提示:可以证明,总是存在合法的解。

思路

H a n d I n D e v i l \sf HandInDevil HandInDevil 太强了!他究竟是怎么想到的啊, o r z orz orz

按照某个大佬的讲法,“黑白两个树加起来就 2 n − 2 2n-2 2n2 条边了,一共 2 n 2n 2n 询问,基本上就是每次询问都需要有效。怎么才能做到这一点呢?考虑让连边的顺序是 距离从小到大,然后并查集判一判就过了……”

好吧,上面那个太毒瘤。我们来听听这个:总操作次数是线性,考虑 O ( 1 ) \mathcal O(1) O(1) 的代价 去掉某个点。如果随意选择一个,你会发现,它很难接入。但是, ⟨ x − 1 , x ⟩ ,    ⟨ x , x + 1 ⟩ \langle x-1,x\rangle,\;\langle x,x+1\rangle x1,x,x,x+1 是异色的,那么无论如何都可以接入。找到这个点行吗?

一方面,不存在这样的点,就直接构造一个环即可。另一方面,找到这个点并且去掉它,剩下的递归问题中,只有 ⟨ x − 1 , x + 1 ⟩ \langle x-1,x+1\rangle x1,x+1 需要重新询问。所以真的是 O ( 1 ) \mathcal O(1) O(1) 删除点!

加上最开始的 O ( n ) \mathcal O(n) O(n) 基础环,最多询问 2 n − 2 2n-2 2n2 次,足以通过。

代码

题目的要求是使用 gra.hpp 中的 main()

#include "gra.hpp"
#include <cstring>
#include <algorithm>
#include <cstdio>
using namespace std;

namespace Stupid{
	const int MaxN = 1002;
	char maze[MaxN][MaxN];
	int getInfo(int a,int b){
		static bool done = false;
		if(!done){
			done = true;
			for(int i=0; i<MaxN; ++i)
			for(int j=0; j<MaxN; ++j)
				maze[i][j] = 2;
		}
		if(a > b) swap(a,b);
		if(maze[a-1][b-1] != 2)
			return maze[a-1][b-1];
		return maze[a-1][b-1] = query(a,b);
	}
	int nxt[MaxN];
	int solve(int n,int begin = 1){
		if(n == 2){
			report(begin,nxt[begin]);
			return getInfo(begin,nxt[begin]);
		}
		for(int i=1,x=begin; i<n; ++i,x=nxt[x]){
			int y = nxt[x], z = nxt[y];
			if(getInfo(x,y) != getInfo(y,z)){
				nxt[x] = z; // erase y
				if(solve(n-1,z) == getInfo(x,y))
					z = x; // link <x,y>
				report(z,y); return getInfo(z,y);
			}
		}
		for(int i=1,x=begin; i<n; ++i)
			report(x,nxt[x]), x = nxt[x];
		return getInfo(begin,nxt[begin]);
	}
}
void tree(int n){
	for(int i=1; i<=n; ++i)
		Stupid::nxt[i] = i+1;
	Stupid::nxt[n] = 1; // form a circle
	Stupid::solve(n);
}

后记

知乎不可挡,乒乓亦难埋。本无一着错,已为人上人。——《对 H a n d I n D e v i l \sf HandInDevil HandInDevil 的判词》

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值