首页 > 代码库 > 【bzoj 1202】[HNOI2005] 狡猾的商人(图论--带权并查集+前缀和)

【bzoj 1202】[HNOI2005] 狡猾的商人(图论--带权并查集+前缀和)

题意:一个账本记录了N个月以来的收入情况,现在有一个侦探员不同时间偷看到M段时间内的总收入,问这个账本是否为假账。

解法:带权并查集+前缀和。
   判断账本真假是通过之前可算到的答案与当前读入的值是否相同来完成。那么就是只有知道新读入的区间2端的(在相同区域内的!!)前缀和才可以判断,也就是这2个端点之前被纳入了相同的区域内才可以判断。于是,我们就可以想到并查集了。(( ′? ??`) 真的么......)
   假设已知x~y月的总收入为d,那么s[y]-s[x-1]=d。一般前缀和是算上自己的,这里我理解s[i]为在 i 所在的区域内 i 之前的数的大小。接着,当x,y不在相同区域内时就将其合并,在时就判断。

P.S.网上似乎还有人用查分约束的方法做。

技术分享
 1 #include<cstdio> 2 #include<cstdlib> 3 #include<cstring> 4 #include<iostream> 5 using namespace std; 6  7 const int N=105,M=1005; 8 int n,m; 9 int fa[N],f[N];10 bool flag;11 12 int ffind(int x)13 {14     if (fa[x]!=x)15     {16       int fx=fa[x];17       fa[x]=ffind(fx);18       f[x]+=f[fx];19     }20     return fa[x];21 }22 void ins(int x,int y,int d)23 {24     int fx=ffind(x),fy=ffind(y);25     if (fx!=fy)26     {27       fa[fx]=fy;//at ease28       f[fx]=f[y]+d-f[x];29     }30     else if (f[x]-f[y]!=d) flag=false;31 }32 int main()33 {34     int T;35     scanf("%d",&T);36     while (T--)37     {38       int x,y,d;39       scanf("%d%d",&n,&m);40       flag=true;41       for (int i=0;i<=n;i++) fa[i]=i,f[i]=0;42       for (int i=1;i<=m;i++)43       {44         scanf("%d%d%d",&x,&y,&d);//s[y]-s[x-1]=d45         if (flag) ins(x-1,y,d);46       }47       if (flag) printf("true\n");48       else printf("false\n");49     }50     return 0;51 }
View Code

我再附个图好了,自己真心理解了很久很久~(┬_┬)

技术分享

【bzoj 1202】[HNOI2005] 狡猾的商人(图论--带权并查集+前缀和)