洛谷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
偶尔做做“水题”,也是不错的吧……但愿……

浙公网安备 33010602011771号