[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;
}


posted on 2018-08-23 18:28  autoint  阅读(183)  评论(0编辑  收藏  举报

导航