P5787 二分图 /【模板】线段树分治

①、前置知识:扩展域并查集(种类并查集):
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 了。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值