bzoj1202[HNOI2005]狡猾的商人

bzoj1202[HNOI2005]狡猾的商人

题意:

账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai 。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。给出m段时间内的总收入,判断账本是否合法。

题解:

太神了,并查集还能这么用。每月作为一个节点,同时保存父节点表示的月份到该月份的盈利是多少,每次读入一个区间,如果左右端点在同一个集合内,就直接比较两个节点的权值差(因为路径压缩时已经使它们的父节点相同),注意路径压缩时要先递归再维护权值。如果左右端点在不同集合,则合并,设所在子树被合并的那个端点为a2,另一个为a1,a2的根节点为a5,给出的信息为a3,则a5的权值变为w[a5]=w[a1]-w[a2]+a3。因为:设a1根节点为a4,s[i]表示i的前缀和,则w[a1]=s[a1]-s[a4] w[a2]=s[a2]-s[a5] a3=s[a2]-s[a1] 显然s[a5]-s[a4]=w[a1]-w[a2]+a3=w[a5]。

代码:

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define inc(i,j,k) for(int i=j;i<=k;i++)
 5 using namespace std;
 6 
 7 int fa[1000],w[1000];
 8 int find(int x){if(x==fa[x])return x;else{int y=find(fa[x]); w[x]+=w[fa[x]]; return fa[x]=y;};}
 9 int main(){
10     int t,n,m; scanf("%d",&t);
11     while(t--){
12         scanf("%d%d",&n,&m); inc(i,0,n)fa[i]=i,w[i]=0; bool f=0;
13         inc(i,1,m){
14             int a1,a2,a3; scanf("%d%d%d",&a1,&a2,&a3); a1--;
15             int a4=find(a1),a5=find(a2);
16             if(a4==a5&&a3!=w[a2]-w[a1]){printf("false
");f=1;break;}
17             if(a4!=a5)w[a5]=w[a1]-w[a2]+a3,fa[a5]=a4;
18         }
19         if(!f)printf("true
");
20     }
21 }

20160401

原文地址:https://www.cnblogs.com/YuanZiming/p/5693050.html