双面扑克
题目大意
给你 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() {
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__
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现