当前位置: 代码迷 >> 综合 >> 带权并查集 POJ 1182 食物链
  详细解决方案

带权并查集 POJ 1182 食物链

热度:7   发布时间:2023-12-22 14:17:23.0

题意:

带权并查集:

初学并查集的时候不太理解带权并查集,现在感觉主要是理解一下偏移量 和 路径压缩过程中偏移量的变化,就应该很好理解带权并查集了。

首先考虑路径压缩。

int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);
}

这样可以做到每次find后,x的祖先要么是它本身,要么是它的父亲节点,树的深度不超过2。

 

令val[u]表示节点u到父亲节点fu的偏移量,val[v]表示节点v到父亲节点fv的偏移量。

加入新的边 (u,v,w)时,若令fa[fu]=fv,则有val[fu] = val[v]+w-val[u]

上图中u到v的偏移量就为 val[u]+val[v]+w-val[u] - val[v] = w,即u到新的根节点的偏移量减去v到新的根节点的偏移量,显然与新加入的边效果是一样的。

假设此后加入新的边 (u,v1,w1)

在执行find(u)操作时,进行路径压缩,偏移量的变化其实就是把原来路径上的总偏移量都加上,形成新的偏移量

比如下图val[u]+val[v]+w-val[u]=val[v]+w,路径压缩后u到fv的偏移量就是val[v]+w。

路径压缩后连边又和起始一样了。

int find(int x){if(fa[x]!=x){int tt=fa[x];fa[x]=find(fa[x]);val[x]=val[x]+val[tt];}return fa[x];
}

本题由于三种动物形成了一个环,那么偏移量应该始终在0-2之间,即对3取模。

(吐槽:HDU有些题不提示多组数据;POJ则有很多单组数据的题,输入后可能有些奇奇怪怪的东西,用判断EOF的方式读入就会出错) 

代码:

#include<cstdio>
#include<iostream>
using namespace std;
#define ll long long
#define db double
#define m_p make_pair
#define p_b push_back
#define For(i,a,b) for(int i=a;i<=b;i++)
#define ls (rt<<1)
#define rs ((rt<<1)|1)
#define mst(a,b) memset(a,b,sizeof(a))
const int MAXN=5e4+5;
const db eps=1e-8;
const int INF=0x3f3f3f3f;
const int mod=3;
const int seed=131;
int fa[MAXN],val[MAXN];
int n,k;
void init(int n){For(i,1,n) fa[i]=i,val[i]=0;
}
int find(int x){if(fa[x]!=x){int tt=fa[x];fa[x]=find(fa[x]);val[x]=(val[x]+val[tt])%mod;}return fa[x];
}
int cal(int x,int y){//计算偏移量return (x-y+mod)%mod;
}
int main(){
//	freopen("in.txt","r",stdin);scanf("%d %d",&n,&k);init(n);int opt,u,v,w,fu,fv,ans=0;For(i,1,k){scanf("%d %d %d",&opt,&u,&v);if(u>n||v>n||u<1||v<1){ans++;continue;}fu=find(u),fv=find(v),w=opt-1;//w为u到v的偏移量,恰好是操作数减1if(fu!=fv){fa[fu]=fv;val[fu]=(val[v]+w-val[u]+mod)%mod;}else if(fu==fv&&cal(val[u],val[v])!=w) ans++;}cout<<ans<<"\n";return 0;
}