bzoj1202: [HNOI2005]狡猾的商人(并查集 差分约束)

1202: [HNOI2005]狡猾的商人

Time Limit: 10 Sec  Memory Limit: 162 MB
Submit: 4127  Solved: 1981
[Submit][Status][Discuss]

Description

刁姹接到一个任务,为税务部门调查一位商人的账本,看看账本是不是伪造的。账本上记录了n个月以来的收入情况,其中第i 个月的收入额为Ai(i=1,2,3...n-1,n), 。当 Ai大于0时表示这个月盈利Ai 元,当 Ai小于0时表示这个月亏损Ai 元。所谓一段时间内的总收入,就是这段时间内每个月的收入额的总和。 刁姹的任务是秘密进行的,为了调查商人的账本,她只好跑到商人那里打工。她趁商人不在时去偷看账本,可是她无法将账本偷出来,每次偷看账本时她都只能看某段时间内账本上记录的收入情况,并且她只能记住这段时间内的总收入。 现在,刁姹总共偷看了m次账本,当然也就记住了m段时间内的总收入,你的任务是根据记住的这些信息来判断账本是不是假的。

Input

第一行为一个正整数w,其中w < 100,表示有w组数据,即w个账本,需要你判断。每组数据的第一行为两个正整数n和m,其中n < 100,m < 1000,分别表示对应的账本记录了多少个月的收入情况以及偷看了多少次账本。接下来的m行表示刁姹偷看m次账本后记住的m条信息,每条信息占一行,有三个整数s,t和v,表示从第s个月到第t个月(包含第t个月)的总收入为v,这里假设s总是小于等于t。

Output

包含w行,每行是true或false,其中第i行为true当且仅当第i组数据,即第i个账本不是假的;第i行为false当且仅当第i组数据,即第i个账本是假的。

Sample Input

2
3 3
1 2 10
1 3 -5
3 3 -15
5 3
1 5 100
3 5 50
1 2 51

Sample Output

true
false
 
/*
若出现茅盾,一定是某个直接或间接出现的区间答案有了冲突
如果已知区间和[a,b],[b,c],那么就可以算出区间和[a,c]。
可以用带权并查集维护区间的答案,每个点维护它到根的区间和。
如果两点a,b在同一连通块内,则区间和为dis[b] - dis[a-1]。
根节点dis = 0. 
更新时这个节点的权值更新为它到新根的区间和。
*/
#include<iostream>
#include<cstdio>
#include<cstring>

#define N 107

using namespace std;
int n,m,k,ans,cnt;
int f[N],dis[N];

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

int find(int x)
{
    if(x!=f[x])
    {
        int t=find(f[x]);
        dis[x]+=dis[f[x]];f[x]=t;
    }return f[x];
}

int main()
{
    int T,x,y,z;T=read();
    while(T--)
    {
        n=read();m=read();int flag=0;
        for(int i=0;i<=n;i++) f[i]=i,dis[i]=0;
        for(int i=1;i<=m;i++)
        {
            x=read();y=read();z=read();x--;
            int r1=find(x),r2=find(y);
            if(r1!=r2) f[r2]=r1,dis[r2]=dis[x]-dis[y]-z;
            else if(dis[x]-dis[y]!=z) flag=1;
        }
        if(flag) printf("false
");else printf("true
");
    }
    return 0;
}
/*
差分约束,对于x1-x2=k,那么要同时满足x1-x2>=k和x1-x2<=k,即双向都建边,只是权值一正一负
然后跑SPFA判负环,如果无负环,则没有矛盾边,否则存在矛盾
*/
#include<iostream>
#include<cstdio>
#include<cstring>
#include<queue>

#define N 2007

using namespace std;
int n,m,ans,cnt,opt;
int head[N],q[N],d[N],vis[N];
bool inq[N];
struct edge{
    int u,v,net,w;
}e[N<<1];

inline int read()
{
    int x=0,f=1;char c=getchar();
    while(c>'9'||c<'0'){if(c=='-')f=-1;c=getchar();}
    while(c>='0'&&c<='9'){x=x*10+c-'0';c=getchar();}
    return x*f;
}

inline void add(int u,int v,int w)
{
    e[++cnt].v=v;e[cnt].w=w;e[cnt].net=head[u];head[u]=cnt;
}

void init()
{
    memset(head,0,sizeof head);
    memset(vis,0,sizeof vis);
    memset(e,0,sizeof e);
    cnt=0;opt=0;
}

void spfa(int s)
{
    queue<int>que;memset(d,127/2,sizeof d);
    memset(inq,0,sizeof inq);
    while(!que.empty()) que.pop();
    inq[s]=1;d[s]=0;que.push(s);
    while(!que.empty())
    {
        int u=que.front();que.pop();inq[u]=0;vis[u]++;
        if(vis[u]>n){opt=1;return;}
        for(int i=head[u];i;i=e[i].net)
        {
            int v=e[i].v;
            if(d[v]>d[u]+e[i].w)
            {
                d[v]=d[u]+e[i].w;
                if(!inq[v]) inq[v]=1,que.push(v);
            }
        }
    }
}

int main()
{
    int T,x,y,z;T=read();
    while(T--)
    {
        n=read();m=read();init();
        for(int i=1;i<=m;i++)
        {
            x=read();y=read();z=read();
            add(x-1,y,-z);add(y,x-1,z);
        }
        for(int i=1;i<=n;i++) 
        {
            if(!vis[i]) 
            {
              spfa(i);
              if(opt) break;
            }
        }
        if(opt)printf("false
");
        else printf("true
");
    }
    return 0;
}
折花枝,恨花枝,准拟花开人共卮,开时人去时。 怕相思,已相思,轮到相思没处辞,眉间露一丝。
原文地址:https://www.cnblogs.com/L-Memory/p/7789006.html