【ybt高效进阶 21162】双面扑克(图论模型)(线段树)(并查集)

双面扑克

题目链接:ybt高效进阶 21162

题目大意

给你 n 个牌,正面反面都有数,多次询问,每次问你能不能凑出 l~r 的顺子。

思路

考虑建立图论模型。

如果一个牌的正反面分别是 x,y,就把 x,y 连一条边。
然后考虑怎样是可以凑出顺子的。

我们可以对于考虑每个数在图论模型中的连通块。
如果那个连通块不是所有点都是在顺子中,那你必然可以一条连着顺子中的数和顺子外的数选顺子内的,然后就不断的往外扩展。
那同一个道理,就算整个连通块都是顺子中的,只要它的边数大于等于它的点数,也是可以的。

那似乎很多情况都可以,那什么情况凑不出呢?
那就是这个连通块的点都是数,而且这个连通块是一棵树。
那我们可以用并查集的简单维护找到是树的连通块。

那要怎么快速判断顺子中是否包含了这些树呢?
考虑是数字,那如果这个顺子包含了这个树,这个数的点分别是 a1,a2,...,am
那这个顺子一定有这么一个部分:min{a1,a2,..,am}max{a1,a2,...,an}

那我们可以把树的最大点和最小点找出来(我是直接顺手在并查集中维护),然后就变成了线段覆盖问题,离线然后线段树搞就可以啦。
(注意如果有覆盖到输出的是 No,没有才是 Yes,因为你是看是否会无法凑出)

代码

#include<cstdio> #include<iostream> #include<algorithm> using namespace std; struct tree { int l, r, pl; }a[100001], q[100001]; int n, k, x, y, fa[100001], an; int maxn[100001], minn[100001], qq; bool ntr[100001], ans[100001]; bool cmp(tree x, tree y) { if (x.r != y.r) return x.r < y.r; return x.l < y.l; } int find(int now) { if (fa[now] == now) return now; return fa[now] = find(fa[now]); } void work(int x, int y) {//用并查集来判断是否有树 int X = find(x), Y = find(y); if (X == Y) ntr[X] = 1; fa[X] = Y; ntr[Y] |= ntr[X]; maxn[Y] = max(maxn[Y], maxn[X]); minn[Y] = min(minn[Y], minn[X]); } struct Tree {//离线+线段树解决线段覆盖问题 int a[400001]; void up(int now) { a[now] = a[now << 1] + a[now << 1 | 1]; } void insert(int now, int l, int r, int pl) { if (l == r) { a[now]++; return ; } int mid = (l + r) >> 1; if (pl <= mid) insert(now << 1, l, mid, pl); else insert(now << 1 | 1, mid + 1, r, pl); up(now); } int query(int now, int l, int r, int L, int R) { if (L <= l && r <= R) return a[now]; int mid = (l + r) >> 1, re = 0; if (L <= mid) re += query(now << 1, l, mid, L, R); if (mid < R) re += query(now << 1 | 1, mid + 1, r, L, R); return re; } }T; int main() { // freopen("yy.in", "r", stdin); // freopen("yy.out", "w", stdout); scanf("%d %d", &n, &k); for (int i = 1; i <= n; i++) fa[i] = maxn[i] = minn[i] = i; for (int i = 1; i <= k; i++) { scanf("%d %d", &x, &y); work(x, y); } for (int i = 1; i <= n; i++) if (!ntr[i] && find(i) == i) { a[++an] = (tree){minn[i], maxn[i], -1}; } scanf("%d", &qq); for (int i = 1; i <= qq; i++) { scanf("%d %d", &q[i].l, &q[i].r); q[i].pl = i; } sort(a + 1, a + an + 1, cmp); sort(q + 1, q + qq + 1, cmp); int anow = 1, qnow = 1; for (int i = 1; i <= n; i++) { while (anow <= an && a[anow].r == i) { T.insert(1, 1, n, a[anow].l); anow++; } while (qnow <= qq && q[qnow].r == i) { if (T.query(1, 1, n, q[qnow].l, n)) ans[q[qnow].pl] = 1; qnow++; } } for (int i = 1; i <= qq; i++) if (ans[i]) printf("No\n"); else printf("Yes\n"); return 0; }

__EOF__

本文作者あおいSakura
本文链接https://www.cnblogs.com/Sakura-TJH/p/YBT_GXJJ_21162.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   あおいSakura  阅读(34)  评论(0编辑  收藏  举报
编辑推荐:
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示