[NOI2001]食物链
拆点法
用并查集维护每种动物的同类、天敌、食物群
#include<cstdio>
int fa[300005];
int n,k,ans;
inline int read()
{
int sum=0;
char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9') sum=sum*10+ch-48,ch=getchar();
return sum;
}//读入优化
int find(int x)
{
if(x!=fa[x]) fa[x]=find(fa[x]);
return fa[x];
}//查询
int unity(int x,int y)
{
int r1=find(fa[x]),r2=find(fa[y]);
fa[r1]=r2;
}//合并
int main()
{
int x,y,z;
n=read(),k=read();
for(int i=1;i<=3*n;++i) fa[i]=i; //对于每种生物:设 x 为本身,x+n 为猎物,x+2*n 为天敌
for(int i=1;i<=k;++i)
{
z=read(),x=read(),y=read();
if(x>n||y>n) {ans++; continue;} // 不属于该食物链显然为假
if(z==1)
{
if(find(x+n)==find(y)||find(x+2*n)==find(y)) {ans++; continue;}
//如果1是2的天敌或猎物,显然为谎言
unity(x,y); unity(x+n,y+n); unity(x+2*n,y+2*n);
//如果为真,那么1的同类和2的同类,1的猎物是2的猎物,1的天敌是2的天敌
}
else if(z==2)
{
if(x==y) {ans++; continue;} //其实是废话但是可以稍微省点时间
if(find(x)==find(y)||find(x+2*n)==find(y)) {ans++; continue;}
//如果1是2的同类或猎物,显然为谎言
unity(x,y+2*n); unity(x+n,y); unity(x+2*n,y+n);
//如果为真,那么1的同类是2的天敌,1的猎物是2的同类,1的天敌是2的猎物
}
}
printf("%d\n",ans);
return 0;
}
带权并查集法
- 如果X、Y是同类,可以用并查集并起来
- 但如果对于A、B、C三类分开保存,会存在一定问题
- 因为A、B、C之间存在着连续关系,比如A吃B、B吃C、C吃D,其实可以推导出A、D为同类
- 考虑将关系建成一张图,两个点之间路径的权值由关系决定。
- 如果u、v是同类,那么u->v路径权值为0
- 如果u吃v,那么u->v路径权值为1
- 如果v吃u,那么u->v路径权值为2
- 那么两个点之间的关系可以由两个点之间的路径得出(模3)。
- 但每次查询时找路径会有一个O(N)的复杂度
- 发现两个点之间的所有路径权值都是一样的,所以只需要保留一条
- 两个点之间的关系由这个权值来表示
- 知道了两个点分别与根的关系,就可以知道两个点之间的关系
- 带权并查集压缩路径的时候需要考虑当前点到新根的距离
- 复杂度O(K*alpha(N))
#include<cstdlib>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<ctime>
#include<iostream>
#include<string>
#include<vector>
#include<list>
#include<deque>
#include<stack>
#include<queue>
#include<map>
#include<set>
#include<algorithm>
#include<complex>
#pragma GCC optimize ("O0")
using namespace std;
template<class T> inline T read(T&x){
T data=0;
int w=1;
char ch=getchar();
while(!isdigit(ch))
{
if(ch=='-')
w=-1;
ch=getchar();
}
while(isdigit(ch))
data=10*data+ch-'0',ch=getchar();
return x=data*w;
}
typedef long long ll;
const int INF=0x7fffffff;
const int MAXN=1e5+7;
int fa[MAXN],val[MAXN];
int find(int x)
{
if(fa[x]==x)
return x;
else
{
int fx=fa[x];
fa[x]=find(fa[x]);
val[x]=(val[x]+val[fx])%3;
return fa[x];
}
}
int main()
{
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
int n,k;
read(n);read(k);
for(int i=1;i<=n;++i)
fa[i]=i,val[i]=0;
int ans=0;
while(k--)
{
int opt,x,y;
read(opt);read(x);read(y);
if(x>n||y>n)
{
++ans;
continue;
}
if(opt==1)
{
int fx=find(x),fy=find(y);
if(fx==fy)
{
if((val[x]+3-val[y])%3!=0)
++ans;
}
else
{
fa[fx]=fy;
val[fx]=3-(val[x]+3-val[y])%3;
}
}
else if(opt==2)
{
if(x==y)
{
++ans;
continue;
}
int fx=find(x),fy=find(y);
if(fx==fy)
{
if((val[x]+3-val[y])%3!=1)
++ans;
}
else
{
fa[fx]=fy;
val[fx]=4-(val[x]+3-val[y])%3;
val[fx]%=3;
}
}
}
printf("%d\n",ans);
// fclose(stdin);
// fclose(stdout);
return 0;
}
静渊以有谋,疏通而知事。