并查集

并查集

[NOI2001] 食物链

题目描述

动物王国中有三类动物 \(A,B,C\),这三类动物的食物链构成了有趣的环形。\(A\) 吃 \(B\),\(B\) 吃 \(C\),\(C\) 吃 \(A\)。

现有 \(N\) 个动物,以 \(1 \sim 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\) 句话,输出假话的总数。

输入格式

第一行两个整数,\(N,K\),表示有 \(N\) 个动物,\(K\) 句话。

第二行开始每行一句话(按照题目要求,见样例)

输出格式

一行,一个整数,表示假话的总数。

样例 #1

样例输入 #1

100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5

样例输出 #1

3

提示

对于全部数据,\(1\le N\le 5 \times 10^4\),\(1\le K \le 10^5\)。

扩展域并查集


std

#include<bits/stdc++.h>
using namespace std;
const int N = 5e4+9;
int n,m,cnt;
int fa[N*3];

int get(int x)
{
	if(fa[x] == x)return x;
	return fa[x] = get(fa[x]);
}


int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= n*3;i++)fa[i] = i;
	//A:1~n B:n+1~2*n c:n*2+1~n*3
	while(m--)
	{
		int op,x,y;
		scanf("%d%d%d",&op,&x,&y);
		int fx = get(x),fy = get(y);
		if(x > n || y > n)cnt++;
		else if(op == 1)
		{
			if(fx == get(y+n) || fy == get(x+n))cnt++;
			else
			{
				fa[fy] = fx;
				fa[get(y+n)] = get(x+n);
				fa[get(y+2*n)] = get(x+2*n);
			}
		}
		else
		{
			if(x == y || fx == fy || fy == get(x+n))cnt++;
			else
			{
				fa[get(y+n)] = fx;
				fa[get(y+2*n)] = get(x+n);
				fa[fy] = get(x+2*n);
			}
		}
	}
	
	printf("%d",cnt);
	
	return 0;
}

带权并查集

维护同一连通块中关系有传递性的点

#include <bits/stdc++.h>
using namespace std;
const int N = 5e5+9;
int pa[N],r[N],n,k,ans,x,y,d;
int get(int x)
{
	if(x != pa[x])
	{
		int fx = get(pa[x]);
		r[x] = (r[x]+r[pa[x]])%3;
		pa[x] = fx;
	}
	return pa[x];
}
bool merge(int d,int x,int y)
{
    int fx=get(x),fy=get(y);
    if (fx==fy)
        if ((r[y]-r[x]+3)%3!=d) return 1;
        else return 0;
        
    pa[fy]=fx;
    r[fy]=(r[x]-r[y]+d+3)%3;
    return 0;
}
int main()
{
    scanf("%d %d",&n,&k);
    for (int i=1;i<=n;i++) pa[i]=i;
    for (int i=1;i<=k;i++)
    {
        scanf("%d %d %d",&d,&x,&y);
        if(x>n||y>n||(x==y && d==2))
		{
			ans++;
			continue;
		}
        if (merge(d-1,x,y)) ans++;
    }
    printf("%d",ans);
}

[CEOI1999] Parity Game

题目描述

Alice 和 Bob 在玩一个游戏:他写一个由 \(0\) 和 \(1\) 组成的序列。Alice 选其中的一段(比如第 \(3\) 位到第 \(5\) 位),问他这段里面有奇数个 \(1\) 还是偶数个 \(1\)。Bob 回答你的问题,然后 Alice 继续问。Alice 要检查 Bob 的答案,指出在 Bob 的第几个回答一定有问题。有问题的意思就是存在一个 \(01\) 序列满足这个回答前的所有回答,而且不存在序列满足这个回答前的所有回答及这个回答。

输入格式

第 \(1\) 行一个整数 \(n\),是这个 \(01\) 序列的长度。

第 \(2\) 行一个整数 \(m\),是问题和答案的个数。

第 \(3\) 行开始是问题和答案,每行先有两个整数,表示你询问的段的开始位置和结束位置。然后是 Bob 的回答。odd表示有奇数个 \(1\),even 表示有偶数个 \(1\)。

输出格式

输出一行,一个数 \(x\),表示存在一个 \(01\) 序列满足第 \(1\) 到第 \(x\) 个回答,但是不存在序列满足第 \(1\) 到第 \(x+1\) 个回答。如果所有回答都没问题,你就输出所有回答的个数。

样例 #1

样例输入 #1

10
5
1 2 even
3 4 odd
5 6 even
1 6 even
7 10 odd

样例输出 #1

3

提示

对于 \(100\%\) 的数据,\(1 \le n \leq 10^9\),\(m \leq 5 \times 10^3\)。

带权并查集

#include<bits/stdc++.h>
using namespace std;

const int N = 5e3+9;
int n,m;
unordered_map<int,int> mp;
struct Q
{
	int l,r;
	bool ans;
}a[N];

int t[N<<1],k;

char ch[5];

int fa[N];
bool r[N];

int get(int x)
{
	if(x != fa[x])
	{
		int fx = get(fa[x]);
		r[x] = r[x]^r[fa[x]];
		fa[x] = fx;
	}
	return fa[x];
}

void merge(int x,int y,int ans)
{
	int fx = get(x),fy = get(y);
	if(fx != fy)
	{
		fa[fx] = fy;
		r[fx] = r[x]^ans^r[y];
	}
	return;
}

int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= m;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		t[++k] = a[i].l-1,t[++k] = a[i].r;
		scanf("%s",ch);
		a[i].ans = ch[0]=='o';
	}

	sort(t+1,t+1+k);
	k = unique(t+1,t+1+k)-t-1;
	for(int i = 1;i <= k;i++)mp[t[i]] = i,fa[i] = i;

	for(int i = 1;i <= m;i++)
	{
		int x = mp[a[i].l-1],y = mp[a[i].r],ans = a[i].ans;
		int fx = get(x),fy = get(y);
		if(fx == fy && (r[x]^r[y]) != ans)printf("%d",i-1),exit(0);
		else merge(x,y,ans);
	}
	printf("%d",m);
	return 0;
}

扩展域并查集

#include<bits/stdc++.h>
using namespace std;

const int N = 5e3+9;
int n,m;
unordered_map<int,int> mp;
struct Q
{
	int l,r;
	bool ans;
}a[N];

int t[N<<1],k;

char ch[5];

int fa[N<<1];

int get(int x)
{
	return x==fa[x] ? x : get(fa[x]);
}


int main()
{
	scanf("%d%d",&n,&m);
	for(int i = 1;i <= m;i++)
	{
		scanf("%d%d",&a[i].l,&a[i].r);
		t[++k] = a[i].l-1,t[++k] = a[i].r;
		scanf("%s",ch);
		a[i].ans = ch[0]=='o';
	}
	
	sort(t+1,t+1+k);
	k = unique(t+1,t+1+k)-t-1;
	for(int i = 1;i <= k;i++)mp[t[i]] = i;
	for(int i = 1;i <= k<<1;i++)fa[i] = i;
	
	for(int i = 1;i <= m;i++)
	{
		int x = mp[a[i].l-1],y = mp[a[i].r],ans = a[i].ans;
		int x_= x+k,y_ = y+k;
		if(ans)
		{
			if(get(x)==get(y))printf("%d",i-1),exit(0);
			fa[get(x)] = get(y_);
			fa[get(x_)] = get(y);
		}
		else 
		{
			if(get(x) == get(y_))printf("%d",i-1),exit(0);
			fa[get(x)] = get(y);
			fa[get(x_)] = get(y_);
		}
	}
	printf("%d",m);
	return 0;
}

并查集维护序列连通性

P2391 白雪皑皑

题目背景

“柴门闻犬吠,风雪夜归人”,冬天,不期而至。千里冰封,万里雪飘。空中刮起了鸭毛大雪。雪花纷纷,降落人间。 美能量星球(pty 在 spore 上的一个殖民地)上的人们被这美景所震撼。但是 pty 却不高兴,他不喜欢白色的世界,他觉得这样太单调了。所以他想对雪花进行染色,让世界变得多彩些。

题目描述

现在有 \(n\) 片雪花排成一列。 pty 要对雪花进行 \(m\) 次染色操作,第 \(i\) 次染色操作中,把第 \(((i\times p+q)\bmod n)+1\) 片雪花和第 \(((i\times q+p)\bmod n)+1\) 片雪花之间的雪花(包括端点)染成颜色 \(i\)。其中 \(p,q\) 是给定的两个正整数。他想知道最后 \(n\) 片雪花被染成了什么颜色。没有被染色输出 \(0\)。

输入格式

输入共四行,每行一个整数,分别为 \(n,m,p,q\),意义如题中所述。

输出格式

输出共 \(n\) 行,每行一个整数,第 \(i\) 行表示第 \(i\) 片雪花的颜色。

样例 #1

样例输入 #1

4
3
2
4

样例输出 #1

2
2
3
0

提示

  • 对于 \(20\%\) 的数据满足:\(n,m\leq 1000\)。
  • 对于 \(40\%\) 的数据满足:\(n\leq 8000\),\(m\leq 10^6\)。
  • 对于 \(80\%\) 的数据满足:\(n\leq 5\times 10^5\),\(m\leq 10^7\)。
  • 对于 \(100\%\) 的数据满足:\(1\leq n\leq 10^6\),\(1\leq m\leq 10^7\)。

保证 \(1\leq m\times p+q,m\times q+p\leq 2\times 10^9\)。


并查集可以维护图的连通性,当然类比到序列上也是可行的
用 \(fa[x]\) 表示 \(x\) 后第一个可操作的节点
倒序覆盖跳跃染色


std

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6+9;

int n,m,p,q;

int fa[N],col[N];

int get(int x)
{
	return x == fa[x] ? x : fa[x] = get(fa[x]);
}

pair<int,int> a[N];


int main()
{
    scanf("%d%d%d%d",&n,&m,&p,&q);
    for(int i = 1;i <= n+1;i++)fa[i] = i;
    for(int i = m;i >= 1;i--)
    {
    	int l = (i*p+q)%n+1,r = (i*q+p)%n+1;
    	if(l > r)swap(l,r);
    	int t = get(l);
    	while(t <= r)
    	{
    		col[t] = i;
    		fa[t] = get(t+1);
    		t = get(t);
		}
	}
    
    for(int i = 1;i <= n;i++)printf("%d\n",col[i]);
    
    return 0;
}
posted @ 2022-11-08 07:39  AC7  阅读(64)  评论(0)    收藏  举报