题目:BZOJ1202
解析:
并查集/差分约束。
差分约束是很明显的,对于每次记录之间分别建立
(
s
−
1
,
t
,
v
)
,
(
t
,
s
−
1
,
−
v
)
(s-1,t,v),(t,s-1,-v)
(s−1,t,v),(t,s−1,−v)的有向边,然后SPFA判负环就行了。
为什么可以用并查集呢,是因为存在关系的传递.
一旦已经知道了
s
[
a
]
−
s
[
b
]
,
s
[
b
]
−
s
[
c
]
s[a]-s[b],s[b]-s[c]
s[a]−s[b],s[b]−s[c](
s
s
s为前缀和),再给出一条
[
a
,
c
]
[a,c]
[a,c]就可以判断"账本的真假"了。那么就可以用并查集维护
s
u
m
sum
sum,
s
u
m
[
i
]
sum[i]
sum[i]表示以
i
i
i为右端点与其最左端点之间的差。如果
s
,
t
s,t
s,t在同一个连通块中,那么它们之间的和即为
s
[
s
]
−
s
[
t
−
1
]
=
(
s
[
s
−
1
]
−
s
[
r
t
]
)
−
(
s
[
t
]
−
s
[
r
t
]
)
=
s
u
m
[
s
−
1
]
−
s
u
m
[
t
]
s[s]-s[t-1]=(s[s-1]-s[rt])-(s[t]-s[rt])=sum[s-1]-sum[t]
s[s]−s[t−1]=(s[s−1]−s[rt])−(s[t]−s[rt])=sum[s−1]−sum[t]
代码(差分约束):
#include <bits/stdc++.h>
using namespace std;
const int Max=105;
int t,n,m,size,tag;
int first[Max],dis[Max],v[Max],sum[Max];
struct shu{int to,next,len;}e[4005];
inline int get_int()
{
int x=0,f=1;char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline void clean(){size=tag=0;for(int i=0;i<=n+1;i++) dis[i]=1e9,v[i]=first[i]=sum[i]=0;}
inline void build(int x,int y,int z){e[++size].next=first[x],first[x]=size,e[size].to=y,e[size].len=z;}
inline bool SPFA(int s)
{
queue<int>q;
q.push(s),sum[s]++,dis[s]=0;
while(q.size())
{
int p=q.front();q.pop();v[p]=0;
for(int u=first[p];u;u=e[u].next)
{
int to=e[u].to;
if(dis[to]>dis[p]+e[u].len)
{
dis[to]=dis[p]+e[u].len;
if(!v[to])
{
sum[to]++;
if(sum[to] >= n) return 0;
v[to]=1,q.push(to);
}
}
}
}
return 1;
}
inline void init()
{
t=get_int();
while(t--)
{
n=get_int(),m=get_int();clean();
for(int i=1;i<=m;i++)
{
int x=get_int(),y=get_int(),z=get_int();
build(y,x-1,-z),build(x-1,y,z);
}
for(int i=0;i<=n;i++) build(n+1,i,0);
if(!SPFA(n+1)) puts("false");
else puts("true");
}
}
int main()
{
init();
return 0;
}
代码(并查集):
#include <bits/stdc++.h>
using namespace std;
const int Max=10005;
int n,m,t,tag;
int fa[Max],d[Max];
inline int get_int()
{
int x=0,f=1;char c;
for(c=getchar();(!isdigit(c))&&(c!='-');c=getchar());
if(c=='-') f=-1,c=getchar();
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
return x*f;
}
inline int get(int v)
{
if(fa[v]==v) return v;
int root=get(fa[v]);
d[v]+=d[fa[v]];
return fa[v]=root;
}
inline void init()
{
t=get_int();
while(t--)
{
n=get_int(),m=get_int(),tag=0;
for(int i=0;i<=n;i++) fa[i]=i,d[i]=0;
while(m--)
{
int x=get_int()-1,y=get_int(),z=get_int();
if(get(x)==get(y))
{
if(d[x]-d[y]!=z) tag=1;
}
else
{
int fx=get(x),fy=get(y);
fa[fy]=fx,d[fy]=d[x]-d[y]-z;
}
}
tag?puts("false"):puts("true");
}
}
int main()
{
init();
return 0;
}