【BZOJ】1202: [HNOI2005]狡猾的商人
【题意】w组数据,给定n和m,给出m段区间[s,t](1<=s<=t<=n)的数字和,求是否矛盾。n<100,m<1000,w<100。
【算法】带权并查集
【题解】由于存在负数,唯一判断信息合法性的途径只有多个已知区间能组成一个已知大区间。
假设有n+1个点0~n表示sum[0]~sum[n],对这些点维护带权并查集,令d[x]表示sum[x]-sum[fa[x]]。
一个已知区间实际上是sum[t] - sum[s-1],如果s-1和t不属于同一个集合就并起来,并且更新d。(注意前缀和方向)
如果属于同一个集合就可以判断是否合法。
最后要注意find过程的执行顺序:int t=find(fa[x]); d[x]+=d[fa[x]]; fa[x]=t;
复杂度O(w*m)。
#include<cstdio> #include<cstring> #include<algorithm> using namespace std; const int maxn=110; int fa[maxn],d[maxn]; int n,m,T; int find(int x){ if(fa[x]==x)return x; int t=find(fa[x]); d[x]+=d[fa[x]]; fa[x]=t; return fa[x]; } int main(){ scanf("%d",&T); while(T--){ memset(d,0,sizeof(d)); scanf("%d%d",&n,&m); for(int i=0;i<=n;i++)fa[i]=i; bool flag=0; for(int i=1;i<=m;i++){ int x,y,w; scanf("%d%d%d",&x,&y,&w); x--; int p=find(x),q=find(y); if(p!=q){ fa[q]=p; d[q]=d[x]+w-d[y]; } else if(d[y]-d[x]!=w){flag=1;break;} } if(flag)printf("false\n");else printf("true\n"); } return 0; }