How Many Answers Are Wrong(HDU3038)
题意:
思路:
1.由于有很多对点的关系,建立带权并查集
2. 建立以左端点为子,右端点为祖先的带权并查集。
3. 由图中可知,当[1,4]确定和[3,4]确定时,添加[1,3]就可能会发生logic错误了。
1) 而从[1,2]很难转换到去求[1,4]-[3,4].所以这里有个小技巧,取开区间左端点,转换为求(0,2]=(0,4]-(2,4]的值
2) 又通过观察可得,当区间为(0,3](红线)时由于3为祖先,不在0,4,2这个并查集内,所以不考虑,那什么时候考虑呢??
绿线:当两个区间的,左和右端点是同一个并查集时。
反思:
- 带权并查集的模板
int find(int x)
{
if(x==f[x])return x;
int root=find(f[x]);
dis[x]+=dis[f[x]];
return f[x]=root;
}
- 合并时的代码。
int A=find(a),B=find(b);
f[A]=B;
dis[A]=dis[b]-dis[a]+s;
- 区间的操作时(涉及向量,并查集)可以把某一端点变为开区间。
AC
#include <iostream>
#include <cstdio>
#include <cmath>
#define For(i,x,y) for(register int i=(x); i<=(y); i++)
using namespace std;
typedef long long ll;
const int maxn=2e5+10;
int a[maxn],f[maxn];
ll dis[maxn];
int find(int x)
{
if(x==f[x])return x;
int root=find(f[x]);
dis[x]+=dis[f[x]];
return f[x]=root;
}
int main()
{
int n,m;
while(~scanf("%d%d", &n,&m))
{
For(i,0,n)dis[i]=0,f[i]=i;
int ans=0;
For(i,1,m)
{
int a,b;
ll s;
scanf("%d%d%lld", &a, &b, &s);
a--;
int A=find(a),B=find(b);
//cout<<dis[a]<<' '<<dis[b]<<endl;
//find(a-1),find(b+1);
//cout<<dis[a-1]<<' '<<dis[b+1]<<endl;
// if(dis[b+1])cout<<"ok"<<endl;
//cout<<(dis[a-1]||dis[b+1])<<endl;
if(A==B)
{
if(abs(dis[b]-dis[a])!=s)ans++;
}
else if(A!=B)
{
//if(dis[a-1]||dis[b+1])
//{
//cout<<abs(dis[b+1]-dis[a-1])<<endl;
//if(s!=abs(dis[b+1]-dis[a-1])){ans++;continue;}
//}
f[A]=B;
dis[A]=dis[b]-dis[a]+s;
// cout<<dis[A]<<' '<<A<<endl;
}
}
printf("%d\n", ans);
}
return 0;
}