240. 食物链

题目链接

240. 食物链

动物王国中有三类动物 \(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\) 句话有的是真的,有的是假的。

当一句话满足下列三条之一时,这句话就是假话,否则就是真话。

  1. 当前的话与前面的某些真的话冲突,就是假话;
  2. 当前的话中 \(X\)\(Y\)\(N\) 大,就是假话;
  3. 当前的话表示 \(X\)\(X\),就是假话。

你的任务是根据给定的 \(N\)\(K\) 句话,输出假话的总数。

输入格式

第一行是两个整数 \(N\)\(K\),以一个空格分隔。

以下 \(K\) 行每行是三个正整数 \(D,X,Y\),两数之间用一个空格隔开,其中 \(D\) 表示说法的种类。

\(D=1\),则表示 \(X\)\(Y\) 是同类。

\(D=2\),则表示 \(X\)\(Y\)

输出格式

只有一个整数,表示假话的数目。

数据范围

\(1 \le N \le 50000\),
\(0 \le K \le 100000\)

输入样例:

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

输出样例:

3

解题思路

扩展域并查集

对于一个点可以拆分为三类:同类、天敌类、捕食类,这三种关系都是互斥的关系,即对于 \(x\) 来说,把与 \(x\) 同类的放在一个集合,天敌类放在一个集合,捕食类放在一个集合,现在并查集主要维护这些信息,即对于 \(x\) 来说,如果 \(x\)\(y\) 属于同一类,则将 \(x\)\(y\) 的同类、天敌类、捕食类分别放在一个集合里面,否则如果 \(x\)\(y\),则分别将将 \(x\) 的捕食类跟 \(y\)\(x\) 的天敌类和 \(y\) 的捕食类,\(x\)\(y\) 的天敌类放在一个集合里
另外,判断出现矛盾的情况:

  • \(x\)\(y\) 属于同一类

    • \(x\)\(y\)
    • \(y\)\(x\)
  • \(x\)\(y\)

    • \(x\)\(y\) 属于同一类
    • \(y\)\(x\)
  • 时间复杂度:\(O(n)\)

带边权并查集

对于 \((x,y)\),考虑定义并查集树边权,如果 \(y\)\(x\) 的父亲节点,说明 \(x\) 可以吃 \(y\),判断 \(x\) 跟并查集树根节点 \(rt[x]\) 的关系,如果 \(x\) 到根节点 \(rt[x]\) 的距离 \(d[x]\%3=0\) 说明 \(x\)\(rt[x]\) 是同类,如果 \(d[x]\%3=1\) 说明 \(x\)\(rt[x]\),否则说明 \(rt[x]\)\(x\)
所以并查集合并时要维护这样的信息,当合并时,只需要将需要变化的值加在根节点变化的根节点上,下次查询时所有的 \(d[x]\) 都要加上这样的变化值,注意路径压缩时,当一个根节点被赋予值时,说明树中的所有 \(d[x]\) 都要加上该变化值,故\(d[x]\) 应该加上的是 \(d[fa[x]]\) 而不是直接 \(rt[x]\)
另外:取模可能会出现负数,即不一定有 \(a\%3\leq b\%3\) 等价于 \((a-b)\%3\),因为负数取模是先取模再加上负号

  • 时间复杂度:\(O(n)\)

代码

  • 扩展域并查集
// Problem: 食物链
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/242/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=50005*3;
int n,k,fa[N];
int find(int x)
{
	return x==fa[x]?x:fa[x]=find(fa[x]);
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=3*n;i++)fa[i]=i;
    int res=0;
    while(k--)
    {
    	int d,x,y;
    	scanf("%d%d%d",&d,&x,&y);
    	if(x>n||y>n)
		{
			res++;
			continue;
		}
    	if(d==1)
    	{
    		if(find(x+2*n)==find(y))res++;
    		else if(find(y+2*n)==find(x))res++;
    		else
    			fa[find(x)]=find(y),fa[find(x+n)]=find(y+n),fa[find(x+2*n)]=find(y+2*n);
    	}
    	else
    	{
    		if(x==y)
    		{
    			res++;
    			continue;
    		}
    		if(find(x)==find(y))res++;
    		else if(find(y+2*n)==find(x))res++;
    		else
    			fa[find(y)]=find(x+2*n),fa[find(y+2*n)]=find(x+n),fa[find(x)]=find(y+n);
    	}
    }
    printf("%d",res);
    return 0;
}
  • 带边权并查集
// Problem: 食物链
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/description/242/
// Memory Limit: 64 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

// %%%Skyqwq
#include <bits/stdc++.h>
 
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
 
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
 
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
 
template <typename T> void inline read(T &x) {
    int f = 1; x = 0; char s = getchar();
    while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
    while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
    x *= f;
}

const int N=50005;
int n,k,fa[N],d[N];
int find(int x)
{
	if(x==fa[x])return x;
	int t=find(fa[x]);
	d[x]+=d[fa[x]];
	return fa[x]=t;
}
int main()
{
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;i++)fa[i]=i;
    int res=0;
    while(k--)
    {
    	int op,x,y;
    	scanf("%d%d%d",&op,&x,&y);
    	if(x>n||y>n)
    	{
    		res++;
    		continue;
    	}
    	int px=find(x),py=find(y);
    	if(op==1)
    	{
    		if(px==py&&(d[x]-d[y])%3)res++;
    		else if(px!=py)
    		{
    			fa[px]=py;
    			d[px]=d[y]-d[x];
    		}
    	}
    	else
    	{
    		if(x==y)
    		{
    			res++;
    			continue;
    		}
    		if(px==py&&(d[x]-d[y]-1)%3)res++;
    		else if(px!=py)
    		{
    			fa[px]=py;
    			d[px]=d[y]-d[x]+1;
    		}
    	}
    }
    printf("%d",res);
    return 0;
}
posted @ 2022-11-29 15:25  zyy2001  阅读(265)  评论(0编辑  收藏  举报