洛谷P2024 [NOI2001]食物链 种类并查集

洛谷P2024 [NOI2001]食物链

题目描述

食物链 - 洛谷

\(n\le5*10^4\)

\(k\le10^5\)

Recollection

初中的时候想了一个假掉了的算法想了很久。

刚刚突然想起自己几次尝试这道题的想法有多么无知。

空を辿る路 足跡一つでも

追寻着天空的路上 只存留着我的足迹

この時のはてに貴方が若し居るなら

如果这个时候有你在我身旁的话

少しだけで好い 彷徨う私にも

哪怕只有一点点 即使是处于彷徨中的我

彼方から風の唄を届けて

也听见了从远方传来的风之歌

然后突然有点怆然……

Solution

先说那时的想法为什么假了吧。

只新开三个拓展的节点\(A=50011\),\(B=50012\),\(C=50013\)

每次判断一个动物属于哪一种就看getf(i)==getf(A/B/C)

当然,这是胡思乱想。因为很显然,每个动物没有实际的值\(ABC\),当然不能用这个来判断。

若第一个输入的点对就假设有一个为\(A\)种,也无法维持后续的间断的传递关系。

\(\dots\dots\)

还是不多说过往的愚昧了吧。


因为有三种动物,故开\(3*n\)的并查集。

这个并查集看作有三个维度,每一维都可以看做我们想要的主集。

那么为什么还要3倍的并查集呢?

因为除了想一般的并查集那样维护直接的传递关系,还要维护不同集合之间的传递"不在同一个集合"的关系。

那么结构上像极了余弦定理

\[a^2=b^2+c^2-2bc*cosA\\ b^2=a^2+c^2-2ac*cosB\\ c^2=a^2+b^2-2ab*cosC \]

而我们的种类并查集也一样。

\(x\),\(y\)属于同一集合,那么

\[f_x=f_y\\ f_{x+n}=f_{y+n}\\ f_{x+n+n}=f_{y+n+n} \]

\(x\)\(y\),那么

\[f_{x+n}=f_y\\f_{x+n+n}=f_{y+n}\\f_{x}=f_{y+n+n} \]

美妙的三元变换呢!(雾

Code

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<map>
#include<set>
#include<queue>
#include<vector>
#define IL inline
#define re register
#define LL long long
#define ULL unsigned long long
#ifdef TH
#define debug printf("Now is %d\n",__LINE__);
#else
#define debug
#endif
using namespace std;

template<class T>inline void read(T&x)
{
    char ch=getchar();
    int fu;
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    x*=fu;
}
inline int read()
{
	int x=0,fu=1;
    char ch=getchar();
    while(!isdigit(ch)&&ch!='-') ch=getchar();
    if(ch=='-') fu=-1,ch=getchar();
    x=ch-'0';ch=getchar();
    while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
    return x*fu;
}
int G[55];
template<class T>inline void write(T x)
{
    int g=0;
    if(x<0) x=-x,putchar('-');
    do{G[++g]=x%10;x/=10;}while(x);
    for(int i=g;i>=1;--i)putchar('0'+G[i]);putchar('\n');
}
int f[50010*3];
int n,k,ans;
int getf(int x)
{
	if(x==f[x]) return x;
	return f[x]=getf(f[x]);
}
void merge(int x,int y)
{
	x=getf(x);
	y=getf(y);
	f[x]=y;
}
bool ask(int x,int y)
{
	return getf(x)==getf(y);
}
int main()
{
	n=read();
	k=read();
	for(int i=1;i<=n*3;i++) f[i]=i;
	while(k--)
	{
		int op,x,y;
		cin>>op>>x>>y;
		if(x>n||y>n)
		{
			ans++;
			continue;
		}
		if(op==1)
		{
			if(ask(x,y+n)||ask(x+n,y)) ans++;
			else
			{
				merge(x,y);
				merge(x+n,y+n);
				merge(x+n+n,y+n+n);
			}
		}
		if(op==2)
		{
			if(ask(x,y)||ask(x,y+n)||ask(x+n,y+n+n)) ans++;
			else
			{
				merge(x+n,y);
				merge(x+n+n,y+n);
				merge(x,y+n+n);
			}
		}
	}
	cout<<ans;
	return 0;
}

Memories

偶尔做做“水题”,也是不错的吧……但愿……

posted @ 2020-11-25 22:31  Vanilla_chan  阅读(210)  评论(0)    收藏  举报