【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 ease 28 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]=d 45 if (flag) ins(x-1,y,d); 46 } 47 if (flag) printf("true\n"); 48 else printf("false\n"); 49 } 50 return 0; 51 }
我再附个简图好了,自己真心理解了很久很久~(┬_┬)模版题设这个:【poj 1962】Corporative Network(图论--带权并查集 模版题)
当然画 树 应该比画方块更好,我是因为接触带权并查集的第一题是【poj 1988】Cube Stacking(图论--带权并查集),所以才理解代码这些都是这样看的。这样的图是可以理解的,画树更本质,更好。