• 狡猾的商人


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

     

     

  • 相关阅读:
    使用AsyncTask简单的文件遍历
    如何创建黑名单
    关于PopupMenu的用法
    联系人的搜索和添加
    php获取真实IP地址
    socket.io获取客户端的IP地址(修正官方1.0.4版本BUG)
    ubuntu下安装bind9
    install source nginx 1.6.0
    在Ubuntu下的samba实现
    买了新硬盘,给ESXI的虚拟机挂进真实硬盘。
  • 原文地址:https://www.cnblogs.com/cutepota/p/12483242.html
Copyright © 2020-2023  润新知