1. 程式人生 > >BZOJ 1202 狡猾的商人 差分約束or帶權並查集

BZOJ 1202 狡猾的商人 差分約束or帶權並查集

!= pre false 其中 png 超時 差分約束 樹根 cto

題目鏈接:

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 }

BZOJ 1202 狡猾的商人 差分約束or帶權並查集