HDU 3038 How Many Answers Are Wrong 很有意思的一道并查集问题
题目大意:TT 和 FF玩游戏(名字就值五毛),有一个数列,数列有N个元素,现在给出一系列个区间和该区间内各个元素的和,如果后出现的一行数据和前面一出现的数据有矛盾,则记录下来。求有矛盾数据的数量。
题目思路:刚刚拿到手时一脸懵逼,这是并查集?后来发现还真是并查集 - -!!
如果数据有错那么会是什么情况?
1-10 10
1-5 5
6-10 4
很明显第三行的数据和已知的数据产生了矛盾,我们分析一下矛盾是如何产生的。
我们用v[i]来统计最右端为i的区间和,那么:
第一行数据得知v[10]=10;
第二行数据得知v[5]=5;
第三行数据在与第二行数据合并时发现 V[10]=9.
这样矛盾便产生了。
由于存在集合划分的问题,所以我们理应想到并查集去解决问题。具体问题看代码吧,代码我觉得解释的比较详细了
#include<cstdio> #include<cstdlib> #include<cmath> #include<iostream> #include<algorithm> #include<cstring> #include<vector> #include<queue> #define INF 0x3f3f3f3f #define MAX 1000005 using namespace std; int father[MAX],v[MAX],n,m; int Find(int x) { if(father[x]==-1) return x; int k=Find(father[x]); v[x]+=v[father[x]];//对v[x]进行更新 return father[x]=k; } int main() { int i,j,a,b,c; while(scanf("%d%d",&n,&m)!=EOF) { int ans=0; for(i=0;i<MAX;i++) { father[i]=-1; v[i]=0; } for(i=1;i<=m;i++) { scanf("%d%d%d",&a,&b,&c); a--;//为什么要减一?你知道了区间1-5,和6-10的和,如果你不把6减1,你如何经行下面的更新呢? int x=Find(a); int y=Find(b); if(x!=y)//如果发现两个根节点不相同,则经行更新 { father[y]=x; v[y]=v[a]-v[b]+c;//由式子:v[y]=v[a]-(v[b]-c),化简而得,自己可以画个图推一下,并不难。 } else//如果根节点相同了,我们就可以经行判断了 { if(v[b]-v[a] !=c)//v[b]-v[a]根据之前的条件得到的区间a-b的和,如果这个值并不等于C证明与前面矛盾。 ans++; } } printf("%d\n",ans); } return 0; }