【ybt高效进阶 21162】双面扑克(图论模型)(线段树)(并查集)
双面扑克
题目链接:ybt高效进阶 21162
题目大意
给你 n 个牌,正面反面都有数,多次询问,每次问你能不能凑出 l~r 的顺子。
思路
考虑建立图论模型。
如果一个牌的正反面分别是 \(x,y\),就把 \(x,y\) 连一条边。
然后考虑怎样是可以凑出顺子的。
我们可以对于考虑每个数在图论模型中的连通块。
如果那个连通块不是所有点都是在顺子中,那你必然可以一条连着顺子中的数和顺子外的数选顺子内的,然后就不断的往外扩展。
那同一个道理,就算整个连通块都是顺子中的,只要它的边数大于等于它的点数,也是可以的。
那似乎很多情况都可以,那什么情况凑不出呢?
那就是这个连通块的点都是数,而且这个连通块是一棵树。
那我们可以用并查集的简单维护找到是树的连通块。
那要怎么快速判断顺子中是否包含了这些树呢?
考虑是数字,那如果这个顺子包含了这个树,这个数的点分别是 \(a_1,a_2,...,a_m\)。
那这个顺子一定有这么一个部分:\(\min\{a_1,a_2,..,a_m\}\sim\max\{a_1,a_2,...,a_n\}\)。
那我们可以把树的最大点和最小点找出来(我是直接顺手在并查集中维护),然后就变成了线段覆盖问题,离线然后线段树搞就可以啦。
(注意如果有覆盖到输出的是 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;
}