HDU 3038 How Many Answers Are Wrong(路径压缩并查集)
http://acm.hdu.edu.cn/showproblem.php?pid=3038
题意:
有一个事先已经写好了的由N个整数组成的整数序列(但是你不知道它的内容),现在给出M条语句,形式如下:
X Y V表示第X个数到第Y个数的和为V值。
这M条语句有可能有错误的语句,比如给出了 1 2 10 ,3 4 10 ,又给一句1 4 100 这句明显是错的,所以对于当前语句如果与前面出现的语句冲突,那么该句就是错的且忽略这句继续往下面处理。
最后输出一共有多少条语句错误。
分析:
本题与POJ1733很相似,也是连续区间的并查集问题(但是本题不需要离散化)。
http://blog.csdn.net/u013480600/article/details/21128299
对于所有输入区间u和v,依然把它变成u-1 和v ,表示(u-1,v]这个范围内(第u个数到第v个数)的整数之和为X。
每个数表示并查集的一个节点,并查集节点维护两种信息:
F[i]:表示i节点的父节点编号。
v[i]:表示i节点到i节点父节点之间的的区间(i,F[i]] 半开半闭区间内整数和为v[i]。
对于findset(x)和bind(u,v)操作,直接用路径压缩并查集的方法更新即可。
如果两个点u和v已经在一个连通分量了,我们还给出了他们的和值,那么就通过分量中u与根,v与根的和值来计算出u与v之间的和值,最后与我们给出的他们的和值比较,看是否相同。如果不同,那么就出错。
AC代码:31ms
<span style="font-size:18px;">#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=200000;
int F[MAXN];
int v[MAXN];
int findset(int i)
{
if(F[i]==-1)return i;
int temp = findset(F[i]);
v[i] +=v[F[i]];
return F[i]=temp;
}
void bind(int i,int j,int sum)//i<j但F[i]和F[j]的相对大小未知,将F[j]和F[i]合并,且将根值大的合并到根值小的里面去
{
int fa=findset(i);
int fb=findset(j);
if(fa!=fb)
{
if(fa<fb)
{
F[fb]=fa;
v[fb]=abs( v[i]+sum-v[j] );
}
else if(fa>fb)
{
F[fa]=fb;
v[fa]=abs( v[i]+sum-v[j] );
}
}
}
int main()
{
int n,m;
while(scanf("%d%d",&n,&m)==2)
{
int ans=0;
memset(F,-1,sizeof(F));
memset(v,0,sizeof(v));
while(m--)
{
int a,b,sum;
scanf("%d%d%d",&a,&b,&sum);
a--;//注意
int fa=findset(a);
int fb=findset(b);
if(fa==fb)
{
if(abs( v[a]-v[b] )!=sum)
ans++;
}
else if(fa!=fb)
{
bind(a,b,sum);
}
}
printf("%d\n",ans);
}
}
</span>