并查集

并查集

[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  阅读(27)  评论(0)    收藏  举报