题解 [APIO2022] 游戏

传送门

只有简略框架
核心思路来自这里

称一个区间为 \([能被到的编号最大值,能到的编号最小值]\)
若有边 \((u, v)\),则边的关系长这样image
发现总是用 \(u\) 的左端点更新 \(v\) 的右端点
发现总是用 \(v\) 的右端点更新 \(u\) 的左端点
只知道端点在哪个块中是可行的是因为考虑某个时刻 \(v\) 的后继两端点在同一块内
一定会递归修改到 \(u\)
而链首的左端点是实际值,链尾的右端点是实际值
注意处理边 \((u, v)\) 时要优先递归 \(u\),防止递归 \(v\) 的时候又来更新这条边
现在代码里有一句 if (v<k) --v; 不知道是干什么的
现在的代码 \(v=0\) 时会 RE
复杂度 \(O(n\log k)\)

点击查看代码
#include "game.h"
#include <bits/stdc++.h>
using namespace std;
#define N 300010
#define ll long long
#define fir first
#define sec second
#define pb push_back

int n, k;
pair<int, int> range[N];
vector<int> e[N], re[N];

void init(int n, int k) {
	::n=n; ::k=k;
	for (int i=0; i<k; ++i) range[i]={i, i+1};
	for (int i=k; i<n; ++i) range[i]={-1, k};
}

bool check_vertice(int u);

bool check_edge(int u, int v) {
	if (range[u].sec<=range[v].fir) return 0;
	if (range[u].fir>=range[v].sec) return 1;
	// cout<<"check: ("<<range[u].fir<<','<<range[u].sec<<") & ("<<range[v].fir<<','<<range[v].sec<<")"<<endl;
	if (range[u]==range[v]) return 0;
	if (range[v].sec<=(range[u].fir+range[u].sec)/2) {
		range[u].sec=(range[u].fir+range[u].sec)/2;
		return check_vertice(u);
	}
	else if (range[u].fir>=(range[v].fir+range[v].sec)/2) {
		range[v].fir=(range[v].fir+range[v].sec)/2;
		return check_vertice(v);
	}
	else return 0;
}

bool check_vertice(int u) {
	for (auto v:e[u]) if (check_edge(u, v)) return 1;
	for (auto v:re[u]) if (check_edge(v, u)) return 1;
	return 0;
}

int add_teleporter(int u, int v) {
	// if (u<k && v<k) return v<=u;
	if (v<k) --v;
	e[u].pb(v); re[v].pb(u);
	return check_edge(u, v);
}
posted @ 2022-06-09 22:08  Administrator-09  阅读(1)  评论(0编辑  收藏  举报