BZOJ 1202 狡猾的商人 差分约束or带权并查集

题目链接:

https://www.lydsy.com/JudgeOnline/problem.php?id=1202

题目大意:

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

思路:

一:差分约束系统转化

对于一段区间的和,可以转化成前缀和相减的形式。

比如区间a-b的和为c,也就是sum[b] - sum[a - 1] = c

可以写成两个式子:

sum[b] - sum[a - 1] <= c 

sum[b] - sum[a - 1] >= c

根据差分约束系统式子:

 

也就是a-1到b 权值为c

b到a-1 权值为-c

判断有没有负环,有的话无解,输出false。

二、带权并查集:

也是转化成前缀和的形式。对于每个节点所带的权值cnt[i] = s[root] - s[i]

1、如果x=a-1,y=b在同一子树中,cnt[x] = s[root] - s[x] cnt[y] = s[root] - s[y]

那么cnt[x] - cnt[y] = s[y] - s[x]判断是否等于输入值c。

2、不在同一子树,进行合并。

设fx为x子树根节点 fy为y子树根节点。

有cnt[x] = s[fx] - s[x] cnt[y] = s[fy] = s[y] 目前又给出条件:s[y] - s[x] = z;

将fy并入fx中,那么cnt[fy]应该设置成s[fx] - s[fy]

由上述三个式子可得:cnt[fy]应该设置成cnt[x] - cnt[y] - z

这样带权并查集的合并就写好了。

差分:

 1 #include<bits/stdc++.h>
 2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
 3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
 4 #define Min(a, b) ((a) < (b) ? (a) : (b))
 5 #define Mem(a) memset(a, 0, sizeof(a))
 6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
 7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
 8 #define lson ((o)<<1)
 9 #define rson ((o)<<1|1)
10 #define Accepted 0
11 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
12 using namespace std;
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
17     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 
21 typedef long long ll;
22 const int maxn = 2000 + 10;
23 const int MOD = 1000000007;//const引用更快,宏定义也更快
24 const int INF = 1e9 + 7;
25 const double eps = 1e-6;
26 
27 struct edge
28 {
29     int v, w;
30     edge(){}
31     edge(int v, int w):v(v), w(w){}
32 };
33 vector<edge>e;
34 vector<int>G[maxn];
35 bool inq[maxn];//是否在队列中
36 int d[maxn];
37 int cnt[maxn];//入队次数
38 int n, m;
39 void addedge(int u, int v, int w)
40 {
41     e.push_back(edge(v, w));
42     G[u].push_back(e.size() - 1);
43 }
44 bool SPFA()
45 {
46     queue<int>q;
47     memset(inq, 0, sizeof(inq));
48     memset(cnt, 0, sizeof(cnt));
49     for(int i = 0; i <= n; i++){d[i] = 0; inq[0] = true;q.push(i);}
50     while(!q.empty())
51     {
52         int u = q.front();
53         q.pop();
54         inq[u] = 0;
55         for(int i = 0; i < G[u].size(); i++)
56         {
57             int v = e[G[u][i]].v;
58             int w = e[G[u][i]].w;
59             if(d[v] > d[u] + w)
60             {
61                 d[v] = d[u] + w;
62                 if(!inq[v])
63                 {
64                     q.push(v);
65                     inq[v] = 1;
66                     if(++cnt[v] > n)return true;
67                 }
68             }
69         }
70     }
71     return false;
72 }
73 int main()
74 {
75     int T;
76     scanf("%d", &T);
77     while(T--)
78     {
79         scanf("%d%d", &n, &m);
80         for(int i = 0; i <= n; i++)G[i].clear();
81         e.clear();
82         int u, v, w;
83         for(int i = 1; i <= m; i++)
84         {
85             scanf("%d%d%d", &u, &v, &w);
86             u--;
87             addedge(u, v, w);
88             addedge(v, u, -w);
89         }
90         if(SPFA())puts("false");
91         else puts("true");
92     }
93     return Accepted;
94 }

带权并查集:

 1 #include<bits/stdc++.h>
 2 #define IOS ios::sync_with_stdio(false);//不可再使用scanf printf
 3 #define Max(a, b) ((a) > (b) ? (a) : (b))//禁用于函数,会超时
 4 #define Min(a, b) ((a) < (b) ? (a) : (b))
 5 #define Mem(a) memset(a, 0, sizeof(a))
 6 #define Dis(x, y, x1, y1) ((x - x1) * (x - x1) + (y - y1) * (y - y1))
 7 #define MID(l, r) ((l) + ((r) - (l)) / 2)
 8 #define lson ((o)<<1)
 9 #define rson ((o)<<1|1)
10 #define Accepted 0
11 #pragma comment(linker, "/STACK:102400000,102400000")//栈外挂
12 using namespace std;
13 inline int read()
14 {
15     int x=0,f=1;char ch=getchar();
16     while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
17     while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
18     return x*f;
19 }
20 
21 typedef long long ll;
22 const int maxn = 2000 + 10;
23 const int MOD = 1000000007;//const引用更快,宏定义也更快
24 const int INF = 1e9 + 7;
25 const double eps = 1e-6;
26 
27 int cnt[maxn];//cnt[i]表示s[root] - s[i]
28 int p[maxn];
29 int Find(int x)
30 {
31     if(x == p[x])return x;
32     int tmp = Find(p[x]);//此处不可以先路径压缩,需要更新x之后再进行路径压缩
33     cnt[x] += cnt[p[x]];//一开始 cnt[x] = s[p[x]] - s[x] cnt[p[x]] = s[root] - s[p[x]]
34     p[x] = tmp;         //需要路径压缩转化成 cnt[x] = s[root] - s[x]
35     return p[x];
36 }
37 int main()
38 {
39     int T;
40     scanf("%d", &T);
41     while(T--)
42     {
43         int n, m;
44         scanf("%d%d", &n, &m);
45         for(int i = 0; i <= n; i++)p[i] = i, cnt[i] = 0;
46         int flag = 0;
47         for(int i = 1; i <= m; i++)
48         {
49             int x, y, z;
50             scanf("%d%d%d", &x, &y, &z);
51             x--;
52             int fx = Find(x), fy = Find(y);
53             if(fx != fy)
54             {
55                 //目前已知 s[y] - s[x] = z  cnt[x] = s[fx] - s[x] cnt[y] = s[fy] - s[y]
56                 //将y的根fy并入x的根fx中 那么需要设置cnt[fy] = s[fx] - s[fy]
57                 //所以cnt[fy] = s[fx] - s[fy] = s[x] + cnt[x] - (s[y] + cnt[y]) = cnt[x] - cnt[y] - z
58                 cnt[fy] = cnt[x] - cnt[y] - z;
59                 p[fy] = fx;
60             }
61             else if(cnt[x] - cnt[y] != z)//验证s[y] - s[x] == z 等价于验证 cnt[x] - cnt[y] == z
62             {
63                 flag = 1;
64             }
65         }
66         if(flag)puts("false");
67         else puts("true");
68     }
69     return Accepted;
70 }

 

posted @ 2018-09-20 20:34  _努力努力再努力x  阅读(155)  评论(0编辑  收藏  举报