狡猾的商人

题目大意:某账本上记录了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 }

 

 

原文地址:https://www.cnblogs.com/cutepota/p/12483242.html