poj 1182 食物链 带权并查集
2016-05-17 20:27
399 查看
题目链接:http://poj.org/problem;jsessionid=6DC324CEA3473E8FC76C6BD83E2626DE?id=1182
题意:题目告诉有3种动物,互相吃与被吃,现在告诉你m句话,其中有真有假,叫你判断假的个数(如果前面没有与当前话冲突的,即认为其为真话)。
经典的并查集题目,可以设置权值r[i](i与父亲节点的关系):
0——同类;
1——食物;
2——天敌。
再用由i的关系r[i],父亲的关系r[pre[i]],推出i与爷爷的关系r[i]=(r[pre[i]]+r[i])%3,这个公式可用来路径压缩。
又由儿子到父亲的关系r[i]可以推出父亲到儿子的关系3-r[i]。
同样,合并两个集合时也需要建立两个根节点的关系,假设x的根节点为x1,y的根节点为x2,如果x1!=x2,将x2做为x1的根,更新:
pre[x1]=x2;
r[x1]=(3-r[x]+d+r[y])%3;//三个量分别为从x1到x,再从x到y,再从y到x2的关系
这个公式和上面的公式异曲同工。
题意:题目告诉有3种动物,互相吃与被吃,现在告诉你m句话,其中有真有假,叫你判断假的个数(如果前面没有与当前话冲突的,即认为其为真话)。
经典的并查集题目,可以设置权值r[i](i与父亲节点的关系):
0——同类;
1——食物;
2——天敌。
再用由i的关系r[i],父亲的关系r[pre[i]],推出i与爷爷的关系r[i]=(r[pre[i]]+r[i])%3,这个公式可用来路径压缩。
又由儿子到父亲的关系r[i]可以推出父亲到儿子的关系3-r[i]。
同样,合并两个集合时也需要建立两个根节点的关系,假设x的根节点为x1,y的根节点为x2,如果x1!=x2,将x2做为x1的根,更新:
pre[x1]=x2;
r[x1]=(3-r[x]+d+r[y])%3;//三个量分别为从x1到x,再从x到y,再从y到x2的关系
这个公式和上面的公式异曲同工。
//#include<bits/stdc++.h> #include<iostream> #include<cmath> #include<cstring> #include<cstdio> #define N 55000 using namespace std; int pre ,r ,n,k,ans; int finds(int v) { if(pre[v]==v) return v; int t=finds(pre[v]);//注意一定要先调用finds(pre[v]),否则父节点的关系没有更新 r[v]=(r[pre[v]]+r[v])%3; pre[v]=t; return t; } void unions(int d,int x,int y) { if(x>n||y>n) { ans++; return; } int x1=finds(x); int x2=finds(y); if(x1==x2) { if((r[x]+3-r[y])%3!=d) ans++; return; } pre[x1]=x2; r[x1]=(3-r[x]+d+r[y])%3;//三个量分别为从x1到x,再从x到y,再从y到x2的关系 } int main() { scanf("%d%d",&n,&k); for(int i=0;i<=n;i++) pre[i]=i,r[i]=0; ans=0; for(int i=0;i<k;i++) { int d,x,y; scanf("%d%d%d",&d,&x,&y); unions(d-1,x,y); } cout<<ans<<endl; }
相关文章推荐