【luogu P8326】Fliper(图论)(构造)(欧拉回路)

Fliper

题目链接:luogu P8326

题目大意

平面上有 n 个挡板(分左上到右下和右上到左下),如果小球碰到会 90 度改变轨迹。
然后你要给每个挡板一个颜色,使得对于每个循环的反弹序列,满足每个颜色的挡板出现次数相同而且是偶数。(如果一个挡板在序列中出现两次算两次)
如果无法构造方案输出 -1。

思路

首先考虑找出循环。
那就是把一个挡板分成两个点(两面),然后连一个同色边表示这个边的颜色确定挡板的颜色。
然后你把从一面弹回到令一个地方的一个面的两个点连边,然后把这个给缩点一下。

那就会形成一些大点(表示循环序列),以及一些其他的点(不在循环序列中),那我们把它连向一个 inf 点。

然后考虑染色,会发现你要满足所有的循环序列,如果暴力搞可以解方程但是复杂度也太大了。
考虑找点性质,那这是一个图我们就找点图论的性质。

发现有解的情况我们看看是则那样的,不难想到情况是每个大点的度数要是 8 的倍数。
然后图论中有一个性质就是度数为奇数的点总是有偶数个的。
然后我们就可以得出 inf 点的度数一定也是偶数。(如果是奇数那就只有 1 个点,不是偶数个)
那不难想到一个东西叫做欧拉回路。
考虑找出欧拉回路然后黑白染色,那我们就分成了两种颜色。

那不难接下来想出分出四种的做法:因为这样之后度数是 4 的倍数,所以也是存在欧拉回路的。(根据上面的图论性质也会有 inf 点度数为偶数)
那我们就对分出的两个颜色的图分别再跑一个欧拉回路,就分成四种了。

代码

#include<queue> #include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int N = 5e5 + 1000; /* op=0: / , op=1: \ */ /* i / n+i i \ n+i */ struct node { int x, y, op, id; }a[N]; int n, col[N * 2], tot; int ans[N], KK_, sta[N * 2], du[N * 2]; vector <int> G[N * 2], F[N * 2], F_[N * 2]; bool in[N * 2], use[N * 2]; int zf, re; char c; int read() { zf = 1; re = 0; c = getchar(); while (c < '0' || c > '9') { if (c == '-') zf = -zf; c = getchar(); } while (c >= '0' && c <= '9') { re = (re << 3) + (re << 1) + c - '0'; c = getchar(); } return re * zf; } bool cmpx(node x, node y) {if (x.x != y.x) return x.x < y.x; return x.y < y.y;} bool cmpy(node x, node y) {if (x.y != y.y) return x.y < y.y; return x.x < y.x;} void add(int x, int y) { G[x].push_back(y); G[y].push_back(x); } //0:光线边 //1:同色边 //2:INF边 bool Find; void dfs(int now, int father) { sta[++sta[0]] = now; in[now] = 1; for (int i = 0; i < G[now].size(); i++) if (G[now][i] != father) { if (in[G[now][i]]) { tot++; Find = 1; while (sta[0]) col[sta[sta[0]]] = tot, sta[0]--; return ; } else dfs(G[now][i], now); if (Find) return ; } } void Add(int x, int y, int z) { F[x].push_back(y); if (x != y) F[y].push_back(x); F_[x].push_back(z); if (x != y) F_[y].push_back(z); du[x]++; du[y]++; } void Link() { sort(a + 1, a + n + 1, cmpy); for (int L = 1, R; L <= n; L = R + 1) { R = L; while (R < n && a[R + 1].y == a[L].y) R++; for (int i = L; i < R; i++) { add(n + a[i].id, a[i + 1].id); } } sort(a + 1, a + n + 1, cmpx); for (int L = 1, R; L <= n; L = R + 1) { R = L; while (R < n && a[R + 1].x == a[L].x) R++; for (int i = L; i < R; i++) { if (a[i].op == 0 && a[i + 1].op == 0) add(a[i].id, n + a[i + 1].id); if (a[i].op == 0 && a[i + 1].op == 1) add(a[i].id, a[i + 1].id); if (a[i].op == 1 && a[i + 1].op == 0) add(n + a[i].id, n + a[i + 1].id); if (a[i].op == 1 && a[i + 1].op == 1) add(n + a[i].id, a[i + 1].id); } } for (int i = 1; i <= 2 * n; i++) if (!in[i]) { sta[0] = 0, Find = 0, dfs(i, 0); if (!Find) { while (sta[0]) use[sta[sta[0]]] = 1, sta[0]--; } } for (int i = 1; i <= n; i++) { int x = use[i] ? 2 * n + 1 : col[i]; int y = use[n + i] ? 2 * n + 1 : col[n + i]; Add(x, y, i); } } bool check_ans() { for (int i = 1; i <= tot; i++) if (du[i] % 8 != 0) return 0; return 1; } struct nde { int id, to, nxt; }e[N * 2]; int in_[N * 2], le[N * 2], KK; void run_way(int now) { for (int i = le[now]; i; i = le[now]) { le[now] = e[i].nxt; if (in_[e[i].id]) continue; in_[e[i].id] = 1; run_way(e[i].to); sta[++sta[0]] = e[i].id; } } void get_oula() { memset(in_, 0, sizeof(in_)); sta[0] = 0; run_way(2 * n + 1); for (int i = 1; i <= tot; i++) run_way(i); for (int i = 1; i <= sta[0]; i += 2) in_[sta[i]]++; } void clear() { KK = 0; memset(le, 0, sizeof(le)); } void slove() { clear(); for (int i = 1; i <= 2 * n + 1; i++) for (int j = 0; j < F[i].size(); j++) e[++KK] = (nde){F_[i][j], F[i][j], le[i]}, le[i] = KK; get_oula(); for (int i = 1; i <= n; i++) ans[i] = in_[i] * 2; clear(); for (int i = 1; i <= 2 * n + 1; i++) for (int j = 0; j < F[i].size(); j++) if (ans[F_[i][j]] == 2) e[++KK] = (nde){F_[i][j], F[i][j], le[i]}, le[i] = KK; get_oula(); for (int i = 1; i <= n; i++) if (in_[i]) ans[i] += in_[i] - 2; clear(); for (int i = 1; i <= 2 * n + 1; i++) for (int j = 0; j < F[i].size(); j++) if (ans[F_[i][j]] == 4) e[++KK] = (nde){F_[i][j], F[i][j], le[i]}, le[i] = KK; get_oula(); for (int i = 1; i <= n; i++) if (in_[i]) ans[i] += in_[i] - 2; for (int i = 1; i <= n; i++) printf("%d ", ans[i]); } int main() { // freopen("pinball.in", "r", stdin); // freopen("pinball.out", "w", stdout); n = read(); for (int i = 1; i <= n; i++) { a[i].x = read(); a[i].y = read(); a[i].id = i; char c = getchar(); while (c != '\\' && c != '/') c = getchar(); if (c == '/') a[i].op = 0; else a[i].op = 1; } Link(); if (!check_ans()) {printf("-1"); return 0;} slove(); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/luogu_P8326.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(89)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示