[題解] HDU 3038 How Many Answers Are Wrong
阿新 • • 發佈:2021-09-15
[題解] HDU 3038 How Many Answers Are Wrong
·題目大意
有一段長度為 \(n\) 的序列 \(S\),現在給定一共 \(m\) 條資訊。
每條資訊格式為 \(l\) \(r\) \(v\)
表示 \(sum(l,r)=v\) 。顯然,這 \(m\) 條資訊中有一些是與之前矛盾的,
現在請你輸出一共有多少條矛盾的資訊。
\(1\leq n \leq 2 \times 10^5\) ,\(1\leq m \leq 4 \times 10^4\), 有多組資料
·解題思路
看到這種題目,第一眼想到的就是差分約束,但是首先圖要支援動態維護邊,並且單次時間複雜度為 \(O(nm)\)
既然不能用差分約束,那麼我們就想到了用帶權並查集來維護資訊。
假設我們用 \(fa[x]\) 來表示 \(x\) 的父節點,陣列 \(val[x]\) 來表示節點 \(x\) 到根節點的權值,並且在合併時固定使得 \(fa[x] = y\) 。
首先,我們要把閉區間變為開區間 \([l,r]\) --> \([l,r+1)\),
那麼當我們處理一條資訊 \(l\) , \(r\) , \(v\) 時,有兩種情況:
- 在同一個並查集合,這時如果不矛盾,
則有 \(v + val[r] = val[l]\),
所以只需判斷 \(v == val[l] - val[r]\) - 不在同一個並查集,我們要把它們合併,並把被合併的根節點更新,
這時 \(l\) 到新的根節點的距離 \(d\) 有兩種方法表示:
\(d = v + val[r]\), \(d = val[l] + val[find(l)]\)
其中 \(find(l)\) 為更新前 \(l\) 的根節點。
那麼只需更新 \(val[find(l)] = v + val[r] - val[l]\) 即可。 - 單次時間複雜度為 \(O(n \times \alpha(n))\)
·程式碼實現
#include <iostream> #define reg register using namespace std; namespace io { template<typename T>inline void read(T &x) { char ch, f = 0; x = 0; while (!isdigit(ch = getchar())) f |= ch == '-'; while (isdigit(ch)) x = (x << 1) + (x << 3) + (ch ^ 48), ch = getchar(); x = f? -x: x; } char ch[20]; template<typename T>inline void write(T x) { (x < 0) && (x = -x, putchar('-')); x || putchar('0'); char i = 0; while (x) ch[i++] = x % 10 ^ 48, x /= 10; while (i) putchar(ch[--i]); } } #define rd io::read #define wt io::write const int maxN = 200010; int fa[maxN], val[maxN]; int n, m, ans; int find(int); bool merge(int, int, int); int main() { while(~scanf("%d%d", &n, &m)) { ans = 0; for (reg int i = 1; i <= n + 1; ++i) fa[i] = i, val[i] = 0; for (reg int i = 1, x, y, z; i <= m; ++i) { rd(x); rd(y); rd(z); ans += merge(x, y + 1, z); } wt(ans); putchar('\n'); } return 0; } int find(int x) { if (x == fa[x]) return x; int k = fa[x]; fa[x] = find(fa[x]); val[x] += val[k]; //更新權值並路徑壓縮 return fa[x]; } bool merge(int x,int y,int v) { int a = find(x), b = find(y); if (a ^ b) { //不在同一集合中合併並更新,其中 a ^ b 相當於 a != b fa[a] = b; val[a] = v + val[y] - val[x]; return false; //v + val[y] == val[a] + val[x] } return v ^ val[x] - val[y]; //在同一集合中判斷 }