[JOISC2019] 聚会 题解

随机化好题,但是不会证。


考虑把树看成一条链,链的每个点上缀了一棵树。

那么先随机出两个点 \(x,y\)(实际上随机一个点,另一个点固定似乎更好?),然后对于当前这棵树上的任意点 \(z\),都让他进行一次询问,答案为 \(o=Q(x,y,z)\)

那么当 \(o=z\) 时,显然 \(z\) 在链上,否则 \(z\)\(o\) 的子树中。

对于每个链上的点的子树,递归处理就可以;而链的形态,直接对链进行排序即可,\(cmp\) 函数容易想到,就是 \([Q(x,a,b)=a]\)

排序询问次数为 \(O(l\log l)\),确定子树询问次数为 \(O(m)\),其中 \(l\) 为链长,\(m\) 为当前子树大小。由于度数很小,可以通过,而且非常优秀。

#include<bits/stdc++.h>
#include "meetings.h"
using namespace std;
int Query(int u,int v,int w);
void Bridge(int u,int v);
const int N=2005;int rt;
mt19937 rnd(20100226);
void bridge(int u,int v){
	if(u>v) swap(u,v);
	Bridge(u,v);
}int cmp(int x,int y){
	return Query(rt,x,y)==x;
}void build(vector<int>g,int x){
	int n=g.size();if(n==1) return;
	if(n==2) return bridge(g[0],g[1]);
	int y=g[rnd()%n];vector<int>ve,v[N];
	while(y==x) y=g[rnd()%n];
	ve.push_back(x),ve.push_back(y);
	v[x].push_back(x),v[y].push_back(y);
	for(auto nw:g){
		if(nw==x||nw==y) continue;
		int fa=Query(x,y,nw);
		if(fa==nw) ve.push_back(nw);
		v[fa].push_back(nw);
	}rt=x,sort(ve.begin()+1,ve.end(),cmp);
	for(int i=0;i<ve.size();i++){
		if(i) bridge(ve[i-1],ve[i]);
		build(v[ve[i]],ve[i]);
	}
}void Solve(int n){
	vector<int>g;
	for(int i=0;i<n;i++)
		g.push_back(i);
	build(g,0);
}
posted @ 2024-12-20 15:56  长安一片月_22  阅读(4)  评论(0编辑  收藏  举报