①、前置知识:扩展域并查集(种类并查集):
P2024 [NOI2001]食物链
动物王国中有三类动物 A,B,C,这三类动物的食物链构成了有趣的环形。A 吃 B,吃 C, 吃 A。
现有 N 个动物,以 1 - N 编号。每个动物都是 A,B,C 中的一种,但是我们并不知道
它到底是哪一种。
有人用两种说法对这 N 个动物所构成的食物链关系进行描述:
第一种说法是“1 X Y”,表示 X 和 Y 是同类。
第二种说法是“2 X Y”,表示 X 吃 Y 。
此人对 N 个动物,用上述两种说法,一句接一句地说出 K 句话,这 K 句话有的是真
的,有的是假的。当一句话满足下列三条之一时,这句话就是假话,否则就是真话。
• 当前的话与前面的某些真的话冲突,就是假话
• 当前的话中 X 或 Y 比 N 大,就是假话
• 当前的话表示 X 吃 X,就是假话
你的任务是根据给定的 N 和 K 句话,输出假话的总数。
输入格式
从 eat.in 中输入数据
第一行两个整数,N,K,表示有 N 个动物,K 句话。
第二行开始每行一句话(按照题目要求,见样例)
输出格式
输出到 eat.out 中
一行,一个整数,表示假话的总数。
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#define ll long long
using namespace std;
const int maxn = 200100;
int f[maxn];
int fi(int x)
{
if (x != f[x])
f[x] = fi(f[x]);
return f[x];
}
int n, k;
int pos, x, y;
int main(void)
{
int ans = 0;
scanf("%d%d", &n,&k);
for (int i = 1;i <= n * 3;i++)
f[i] = i;
for (int i = 1;i <= k;i++)
{
scanf("%d%d%d", &pos, &x, &y);
if (x > n || y > n)
{
ans++;
continue;
}
if (pos == 1)
{
if (fi(x + n) == fi(y) || fi(x) == fi(y + n)) ans++;
else
{
f[fi(x)] = fi(y);
f[fi(x + n)] = fi(y + n);
f[fi(x + n * 2)] = fi(y + n * 2);
}
}
else if (pos == 2)
{
if (fi(x) == fi(y) || fi(x) == fi(y + n)) ans++;
else
{
f[fi(x + n)] = fi(y);
f[fi(x + 2 * n)] = fi(y + n);
f[fi(x)] = fi(y + 2 * n);
}
}
}
printf("%d\n", ans);
return 0;
}
②、P5787 二分图 /【模板】线段树分治:
(一)扩展域并查集:
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <stack>
#define ll long long
#define pr make_pair
#define pb push_back
using namespace std;
const int maxn = 100100;
int f[maxn << 1], d[maxn << 1], x[maxn << 1], y[maxn << 1], n, m, k;
stack<pair<int, int> >st;
struct node
{
int l, r;
vector<int>vc;
}t[maxn<<2];
int fi(int x)
{
while (x != f[x]) x = f[x];
return x;
}
void build(int l, int r, int cnt)
{
t[cnt].l = l, t[cnt].r = r;
t[cnt].vc.clear();
if (l == r) return;
int mid = (l + r) >> 1;
build(l, mid, cnt << 1);
build(mid + 1, r, cnt << 1 | 1);
}
void _insert(int l, int r, int cnt, int x)
{
if (l <= t[cnt].l && t[cnt].r <= r)
{
t[cnt].vc.pb(x);
return;
}
if (t[cnt << 1].r >= l) _insert(l, r, cnt << 1, x);
if (t[cnt << 1 | 1].l <= r) _insert(l, r, cnt << 1 | 1, x);
}
void _merge(int x, int y)
{
int xx = fi(x), yy = fi(y);
if (xx == yy) return;
if (d[xx] > d[yy]) swap(xx, yy);
st.push(pr(xx, d[xx] == d[yy]));
f[xx] = yy;
d[yy] += (d[xx] == d[yy]);
}
void dfs(int l, int r, int cnt)
{
bool flag = true;
int now = st.size();
for (int i = 0;i < t[cnt].vc.size();i++)
{
int pos = t[cnt].vc[i];
int nx = fi(x[pos]), ny = fi(y[pos]);
if (nx == ny)
{
for (int j = l;j <= r;j++)
printf("No\n");
flag = false;
break;
}
_merge(x[pos], y[pos] + n);
_merge(x[pos] + n, y[pos]);
}
if (flag)
{
if (l == r) printf("Yes\n");
else
{
dfs(t[cnt << 1].l, t[cnt << 1].r, cnt << 1);
dfs(t[cnt << 1 | 1].l, t[cnt << 1 | 1].r, cnt << 1 | 1);
}
}
while (st.size() > now)
{
d[f[st.top().first]] -= st.top().second;
f[st.top().first] = st.top().first;
st.pop();
}
}
int main(void)
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1;i <= n * 2;i++)
f[i] = i;
build(1, k, 1);
int l, r;
for (int i = 1;i <= m;i++)
{
scanf("%d%d%d%d", &x[i], &y[i], &l, &r);
if (l == r) continue;
_insert(l+1, r, 1, i);
}
dfs(1, k, 1);
return 0;
}
(二)并查集上维护权值
好久没写带权值的并查集了。。都忘干净了。
val表示的是x到f【x】的权值。。。
#include <iostream>
#include <string>
#include <cstdio>
#include <cstring>
#include <vector>
#include <map>
#include <stack>
#define ll long long
#define pr make_pair
#define pb push_back
using namespace std;
const int maxn = 100100;
int f[maxn], d[maxn], val[maxn], x[maxn << 1], y[maxn << 1], n, m, k;
stack<pair<int, int> >st;
struct node
{
int l, r;
vector<int>vc;
}t[maxn<<2];
int fi(int x,int &cnt)
{
while (x != f[x]) cnt += val[x], x = f[x];
return x;
}
void build(int l, int r, int cnt)
{
t[cnt].l = l, t[cnt].r = r;
t[cnt].vc.clear();
if (l == r) return;
int mid = (l + r) >> 1;
build(l, mid, cnt << 1);
build(mid + 1, r, cnt << 1 | 1);
}
void _insert(int l, int r, int cnt, int x)
{
if (l <= t[cnt].l && t[cnt].r <= r)
{
t[cnt].vc.pb(x);
return;
}
if (t[cnt << 1].r >= l) _insert(l, r, cnt << 1, x);
if (t[cnt << 1 | 1].l <= r) _insert(l, r, cnt << 1 | 1, x);
}
void _merge(int xx, int yy,int nx,int ny)
{
if (xx == yy) return;
if (d[xx] > d[yy]) swap(xx, yy),swap(nx,ny);
st.push(pr(xx, d[xx] == d[yy]));
//cout << "xx: " << xx << " yy: " << yy << endl;
f[xx] = yy;
val[xx] = -val[nx] + val[ny] + 1;
d[yy] += (d[xx] == d[yy]);
}
void dfs(int l, int r, int cnt)
{
bool flag = true;
int now = st.size();
for (int i = 0;i < t[cnt].vc.size();i++)
{
int pos = t[cnt].vc[i], cntx = 0, cnty = 0;
int nx = fi(x[pos],cntx), ny = fi(y[pos],cnty);
//cout << x[pos] << " " << val[pos[x]] << " " << y[pos] << " " << val[pos[y]] << endl;
//cout << f[x[pos]] << " " << f[y[pos]] << endl;
if (nx == ny && (cntx+cnty) % 2 == 0)
{
for (int j = l;j <= r;j++)
printf("No\n");
flag = false;
break;
}
_merge(nx, ny,pos[x],pos[y]);
}
if (flag)
{
if (l == r) printf("Yes\n");
else
{
dfs(t[cnt << 1].l, t[cnt << 1].r, cnt << 1);
dfs(t[cnt << 1 | 1].l, t[cnt << 1 | 1].r, cnt << 1 | 1);
}
}
while (st.size() > now)
{
d[f[st.top().first]] -= st.top().second;
f[st.top().first] = st.top().first;
val[st.top().first] = 0;
st.pop();
}
}
int main(void)
{
scanf("%d%d%d", &n, &m, &k);
for (int i = 1;i <= n;i++)
f[i] = i;
build(1, k, 1);
int l, r;
for (int i = 1;i <= m;i++)
{
scanf("%d%d%d%d", &x[i], &y[i], &l, &r);
if (l == r) continue;
_insert(l+1, r, 1, i);
}
dfs(1, k, 1);
return 0;
}
据说 lct 也能写。。
再说吧,已经好久没写过 lct 了。