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\n");f=1;break;} 17 if(a4!=a5)w[a5]=w[a1]-w[a2]+a3,fa[a5]=a4; 18 } 19 if(!f)printf("true\n"); 20 } 21 }
20160401