题目大意:某账本上记录了n个月以来的收入情况,正为盈利,负为亏损。某人的秘密任务是调查账本的真假。她偷看了m次账本,每次她只能看账本上某段时间的收入情况,并记住这段时间的总收入。你的任务是根据这些信息,判断账本是不是假的。当然,本题给你你w个账本等着你判断。
样例分析:
输入:
2 //2组询问
3 3 //3个月,偷看了3次账本
1 2 10 //1-2月总收入为10
1 3 -5
3 3 -15
5 3 //第二组询问
1 5 100
3 5 50
1 2 51
输出:
true //第一个账本是真的
false//第二个账本是假的
sol:本题给出了某些区间段的总收入,我们可以定义一个树上的部分和出来。根据数的部分和的求法,sum[j-i]=sum[j]-sum[i-1],本题我们用dist[i]表示结点i到连通块根结点的距离,用来代表根结点+1月到第i月的总收入,这样可以将月份之间的关系组织成一棵树的形式。于是我们可以快速知道[i,j]这一段的距离值,从而知道i+1月到j月这个区间的总收入。从而判断账本的真假。注意两个重要的实现:
1.求连通块某个点,到根结点的距离。可在递归找某个点所在连通块的根结点的同时就求出来。
1 int get(int x) //求出点所在连通块的根,并求出点到它的距离
2 {
3 if(fa[x]==x)return x;
4 int y=fa[x];
5 fa[x]=get(fa[x]);
6 dis[x]+=dis[y];
7 return fa[x];
8 }
2.处理区间两个点,建立并查集。实现 merge(x,y,z)。
(1) 两个点x,y已经在一个集合,说明dis[x]与dis[y]前面已经求得,此时可计算出 dis[y]-dis[x],对比下是否等于读入的收入z。
(2) 两个点不在一个集合,则进行两个集合的合并,这时要判断两个点所在集合的根谁大谁小,将小的作为合并后的根,同时更新大的那个点到新根的距离。
1 bool merge(int x,int y,int z)
2 // x月到y月的收入为z ,x<=y
3 {
4 int fx=get(x),fy=get(y);
5 if(fx==fy)
6 //如果已在一个连通块中,则两点间的距离差就知道了,对比下输出的情况看符合不
7 return dis[y]-dis[x]==z;
8 if(fx<fy) //更新某个点到root的距离
9 fa[fy]=fx,dis[fy]=dis[x]+z-dis[y];
10 //注意此处是更新y的父亲点fy到新的祖先点fx的距离
11 else
12 fa[fx]=fy,dis[fx]=dis[y]-z-dis[x];
13 return 1;
14 }
完整代码如下:
1 #include<cstdio>
2 #include<cstring>
3 #include<algorithm>
4 #define maxn 106
5 using namespace std;
6 inline char nc(){
7 static char buf[100000],*i=buf,*j=buf;
8 return i==j&&(j=(i=buf)+fread(buf,1,100000,stdin),i==j)?EOF:*i++;
9 }
10 inline int _read(){
11 char ch=nc();int sum=0,p=1;
12 while(ch!='-'&&!(ch>='0'&&ch<='9'))ch=nc();
13 if(ch=='-')p=-1,ch=nc();
14 while(ch>='0'&&ch<='9')sum=sum*10+ch-48,ch=nc();
15 return sum*p;
16 }
17 int T,n,m,fa[maxn],dis[maxn];
18 int get(int x) //求出点所在连通块的根,并求出点到它的距离
19 {
20 if(fa[x]==x)return x;
21 int y=fa[x];
22 fa[x]=get(fa[x]);
23 dis[x]+=dis[y];
24 return fa[x];
25 }
26 bool merge(int x,int y,int z)
27 // x月到y月的收入为z ,x<=y
28 {
29 int fx=get(x),fy=get(y);
30 if(fx==fy)
31 //如果已在一个连通块中,则两点间的距离差就知道了,对比下输出的情况看符合不
32 return dis[y]-dis[x]==z;
33 if(fx<fy) //更新某个点到root的距离
34 fa[fy]=fx,dis[fy]=dis[x]+z-dis[y];
35 //注意此处是更新y的父亲点fy到新的祖先点fx的距离
36 else
37 fa[fx]=fy,dis[fx]=dis[y]-z-dis[x];
38 return 1;
39 }
40 int main(){
41
42 T=_read();
43 while(T--){
44 n=_read();m=_read();bool ans=1;
45 for(int i=0;i<=n;i++)
46 fa[i]=i,dis[i]=0;
47 for(int i=1;i<=m;i++)
48 {
49 int x=_read(),y=_read(),z=_read();
50 if(!merge(x-1,y,z))
51 ans=0;
52 }
53 if(ans)printf("true
");
54 else printf("false
");
55 }
56 return 0;
57 }