[关键字]:并查集
[题目大意]:有一本记录了n个月的账本,偷看了m次每次记住了一个区间[x,y]的和,问这本帐是否是假账。
//===================================================================================
[分析]:首先可以想到要判断假账必须是某个区间[l,r]出现了两个值——一个他给的值、一个依靠别的计算出的值。所以可以一个集合里面的点代表的一个连续的区间,如果两个点不在一个集合内就合并,否则判断前y个月的值-前x个月的值是否是给出的值。用g[x]记录x到他所在的集合最后一个点也就是他所在的连续区间最左边的点的总和的相反数,这样对于同一个集合内的x、y,设这个集合的最后一个点是v,g[y]-g[x]=-sum(y,v)-(-sum(x,v))=sum(x,v)-sum(y,v)=x、y的盈利,而合并时:g[y]-g[x]-dis(x、y的盈利)=-sum(y,vy)+sum(x,vy)-dis=dis+sum(y,vx)-sum(y,vx)-sum(vx,vy)-dis=-sum(vx,vy),这时再把f[vx]=f[vy]就完成了合并和g的维护,压缩时也要维护g。
[代码]:
View Code
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
using namespace std;
const int MAXN=1000;
int w,n,m;
bool can;
int f[MAXN],g[MAXN];
int Find(int k)
{
if (f[k]==k) return k;
Find(f[k]);
g[k]+=g[f[k]];
return f[k]=f[f[k]];
}
void Union(int x,int y,int z)
{
int xx=Find(x),yy=Find(y);
if (xx!=yy)
{
f[xx]=yy;
g[xx]=g[y]-g[x]-z;
}
else
if (g[y]-g[x]!=z) can=0;
}
int main()
{
scanf("%d",&w);
while (w--)
{
scanf("%d%d",&n,&m);
can=1;
for (int i=0;i<=n;i++) f[i]=i,g[i]=0;
for (int i=1;i<=m;i++)
{
int x,y,z;
scanf("%d%d%d",&x,&y,&z);
Union(x-1,y,z);
}
if (can) printf("true\n"); else printf("false\n");
}
return 0;
}