[COCI2021-2022#6] Naboj 题解

前言

题目链接:洛谷

题意简述

给定一张无向图,每条边有个哨兵,初始在边的中间。你可以把某个结点旁边的哨兵全部吸引或远离这个结点。给出最后每个哨兵在边的哪一端,请构造出一种可能的操作方案或报告无解。多种情况输出任意解,你不需要最小化操作步数

题目分析

发现一个哨兵和且仅和最后一次关联这条边的操作有关,考虑使用“浮水法”反转操作,即假定一个哨兵在被定向后就不会再发生变化,那么把这样的方案倒序输出就是原问题答案了。

那么如何求解这个问题呢?发现一开始操作的点一定是哨兵最终要全部靠近或远离这个点的,为简化讨论,不妨令一开始操作的点连出的所有边,哨兵都在这个点一侧。那么给它设为吸引哨兵后,它对其他节点就没有了影响。所以考虑删去这个点和其连出的边,循环此过程。

发现这就是在跑一个拓扑排序。既然是拓扑排序,无解的时候当且仅当出现了环,也就是最后还有的边不能被确定。

于是这道题就愉快地过掉啦。

代码(已略去快读快写)

目前最优解 rank1

//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;

int n, m;

struct Graph{
	struct node{
		int to, nxt;
	} edge[500010 << 1];
	int eid, head[500010];
	inline void add(int u, int v){
		edge[++eid] = {v, head[u]};
		head[u] = eid;
	}
	inline node & operator [] (const int x){
		return edge[x];
	}
} xym;

int du[500010], Q[500010], top;
int ans[500010], tot;

signed main(){
	read(n, m);
	for (int i = 1, u, v; i <= m; ++i) read(u, v), xym.add(u, v), ++du[v];
	for (int i = 1; i <= n; ++i) !du[i] && (Q[++top] = i);
	while (top){
		int now = Q[top--]; ans[++tot] = now;  // 给这个点设为吸引哨兵
		for (int i = xym.head[now]; i; i = xym[i].nxt)
			if (!--du[xym[i].to]) Q[++top] = xym[i].to;  // 拓扑排序
	}
	for (int i = 1; i <= n; ++i) du[i] && (write(-1), exit(0), yzh_i_love_you);
	// 无解当且仅当最后出现了环
	write(tot, '\n');
	for (int i = tot; i >= 1; --i) write(ans[i], ' ', 1, '\n');  // 逆序输出
	return 0;
}
完整代码
//#pragma GCC optimize(3)
//#pragma GCC optimize("Ofast", "inline", "-ffast-math")
//#pragma GCC target("avx", "sse2", "sse3", "sse4", "mmx")
#include <iostream>
#include <cstdio>
#define debug(a) cerr << "Line: " << __LINE__ << " " << #a << endl
#define print(a) cerr << #a << "=" << (a) << endl
#define file(a) freopen(#a".in", "r", stdin), freopen(#a".out", "w", stdout)
#define main Main(); signed main(){ return ios::sync_with_stdio(0), cin.tie(0), Main(); } signed Main
using namespace std;

namespace Fast{
	constexpr const int MAX = 1 << 24, yzh_i_love_you = 1314520736;
	
	#ifndef LOCAL
	inline char getchar(void){
		static char buf[MAX], *p1 = buf, *p2 = buf;
		return (p1 == p2) && (p2 = (p1 = buf) + fread(buf, 1, MAX, stdin), p1 == p2) ? EOF : *p1++;
	}
	#endif
	
	char obuf[MAX], *o = obuf;
	inline void flush(void){
		fwrite(obuf, 1, o - obuf, stdout), o = obuf;
	}
	inline void putchar(const char c){
		(*o++ = c, o - obuf == MAX) && (flush(), yzh_i_love_you);
	}
	
	template <typename T> constexpr inline T lowbit(T x) { return x & -x; }
	template <typename T> constexpr inline T abs(T x) { return x > 0 ? x : -x; }
	template <typename T1, typename T2> constexpr inline auto max(T1 a, T2 b) { return a > b ? a : b; }
	template <typename T1, typename T2> constexpr inline auto min(T1 a, T2 b) { return a < b ? a : b; }
	template <typename T, typename... Types> constexpr inline auto max(T a, Types... b) { auto res = max(b...); return res > a ? res : a; }
	template <typename T, typename... Types> constexpr inline auto min(T a, Types... b) { auto res = min(b...); return res < a ? res : a; }
	constexpr inline bool isdigit(const char c) { return c >= '0' && c <= '9'; }
	constexpr inline bool empty(const char c){ return c == '\r' || c == '\n' || c == ' ' || c == '\t' || c == '\0' || c == EOF; }
	
	inline int read(){ return 0; } template <typename T, typename... Types>
	inline int read(T &x, Types&... args) {
		if constexpr (is_same<typename decay<T>::type, char>::value)
		{ for (x = getchar(); empty(x); x = getchar()) if (x == EOF) return read(args...), EOF; }
		else if constexpr (is_same<typename decay<T>::type, char*>::value){
		for (x[0] = 0; empty(x[0]); x[0] = getchar()) if (x[0] == EOF) return x[0] = 0, read(args...), EOF;
		for (unsigned i = 0;; x[++i] = getchar()) if (empty(x[i])) return x[i] = 0, read(args...);
		} else { x = 0; char c = 0, f = 0;
	    for (;!isdigit(c); c = getchar()) if (f |= c == '-', c == EOF) return read(args...), EOF;
	    for (; isdigit(c); c = getchar()) x = (x << 3) + (x << 1) + (c ^ 48);
	    f && (x = -x); } return read(args...);
	}
	
	inline void write(){} template <typename T, typename... Types>
	inline void write(T x, Types... args){
		if constexpr (is_same<typename decay<T>::type, char>::value)
		return putchar(x), write(args...);
		if constexpr (is_same<const char*, T>::value || is_same<char*, T>::value)
		{ for (unsigned i = 0; x[i]; putchar(x[i++])); } else
		{ static short Stack[50], top = 0;
		(x < 0) && (putchar('-'), yzh_i_love_you);
		do Stack[++top] = x % 10, x /= 10; while (x);
	    while (top) putchar(abs(Stack[top--]) | 48); } write(args...);
	}
	
	#undef main
	#define main Main(); signed main() { return atexit(flush), Main(); } signed Main
}

using namespace Fast;

int n, m;

struct Graph{
	struct node{
		int to, nxt;
	} edge[500010 << 1];
	int eid, head[500010];
	inline void add(int u, int v){
		edge[++eid] = {v, head[u]};
		head[u] = eid;
	}
	inline node & operator [] (const int x){
		return edge[x];
	}
} xym;

int du[500010], Q[500010], top;
int ans[500010], tot;

signed main(){
	read(n, m);
	for (int i = 1, u, v; i <= m; ++i) read(u, v), xym.add(u, v), ++du[v];
	for (int i = 1; i <= n; ++i) !du[i] && (Q[++top] = i);
	while (top){
		int now = Q[top--]; ans[++tot] = now;  // 给这个点设为吸引哨兵
		for (int i = xym.head[now]; i; i = xym[i].nxt)
			if (!--du[xym[i].to]) Q[++top] = xym[i].to;  // 拓扑排序
	}
	for (int i = 1; i <= n; ++i) du[i] && (write(-1), exit(0), yzh_i_love_you);
	// 无解当且仅当最后出现了环
	write(tot, '\n');
	for (int i = tot; i >= 1; --i) write(ans[i], ' ', 1, '\n');  // 逆序输出
	return 0;
}

总结 & 后话

对于这种最终结果依赖于最后一次操作的时候,考虑使用“浮水法”,反转操作顺序,它被操作后就不再变化了,而实际操作顺序就是新操作顺序的逆序。

posted @ 2024-03-31 22:25  XuYueming  阅读(81)  评论(0)    收藏  举报