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;
}