HNOI2005狡猾的商人

2021-08-08 22:19:35 字數 1734 閱讀 7966

傳送門

題目意思很簡單,意思是說給你一些區間和,要你判斷這些區間和是否合法。

開始只想到了差分約束的方法,就是搞成字首和的形式 su

m[r]

−sum

[l−1

]>=

w 且 su

m[r]

−sum

[l−1

]<=

w 這樣利用spfa建圖,利用三角形不等式,即dis[v] > dis[x] + w[i],每個條件建出sum[r]->sum[l-1] 邊權為w,sum[l-1]->sum[r]邊權為-w兩條邊就好了。

看了下並查集的做法,感覺有點深奧,思考了一番,發現其實很好理解。

我們不難發現,如果你知道l到點p之間的和,又知道r到p的和,現在給你l~r之間的和,那是不是就可以判斷合法了呢?於是問題就轉化成這樣,那我們要如何求呢?我們可以把每個條件看成是l-1到r的一條權值為w的邊,我們維護乙個陣列c代表當前點到這個聯通快的根的距離,如果l-1和r在乙個集合裡就可以判斷了。

否則我們把兩個集合合併,我們知道c[r]-c[l] = w,那麼c[root[r]] = c[l] - c[r] - w(手推一下)。當時我看到這裡我有個疑問.為什麼只更新r所在聯通快的根節點?r到新根節點的距離不是改變了嗎?相信不少人也會有這個疑問。但是實際上是沒事的,因為當前r還是接在他本來的祖先下邊,我們find的時候路徑壓縮的時候會維護當前點的c陣列,如果現在計算了,之後會重複算。

#include

#include

#include

#include

#include

#include

using

namespace

std;

typedef

long

long ll;

#define rep(i,a,b) for(register int i = (a), i##_end_ = (b); i <= i##_end_; ++i)

#define drep(i,a,b) for(register int i = (a),i##_end_ = (b); i >= i##_end_; --i)

template

inline

bool chkmin(t &a,const t &b)

template

inline

bool chkmax(t &a,const t &b)

int read()

while(isdigit(c))

return f * s;

}const

int maxn = 100010;

int t,n,m;

int s[maxn],fa[maxn];

//用s維護與當前聯通快的根節點的差,即s[x] - s[fa[x]]

//考慮為什麼在乙個聯通塊裡就可以直接求出答案?

//因為當前給出的l,r,w,如果我知道l到某個點的差值,r到某個點的差值,我可以直接判斷

//所以如果在同乙個集合裡,直接算

//不在的話,我們可以以任意乙個為根節點來合併

//並查集路徑壓縮的時候要記得維護差值資訊

int find(int x)

int main()

else

}if(flag)puts("false");

else

puts("true");

}return

0;}

HNOI2005 狡猾的商人

刁奼接到乙個任務,為稅務部門調查一位商人的賬本,看看賬本是不是偽造的。賬本上記錄了n個月以來的收入情況,其中第i 個月的收入額為ai i 1,2,3 n 1,n 當 ai大於0時表示這個月盈利ai 元,當 ai小於0時表示這個月虧損ai 元。所謂一段時間內的總收入,就是這段時間內每個月的收入額的總和...

HNOI2005 狡猾的商人

hnoi2005 狡猾的商人 time limit 10 sec memory limit 162 mb description 刁奼接到乙個任務,為稅務部門調查一位商人的賬本,看看賬本是不是偽造的。賬本上記錄了n個月以來的收入情況,其中第i 個月的收入額為ai i 1,2,3 n 1,n 當 ai...

HNOI2005 狡猾的商人

本來是要做帶權並查集才跳到這個題上的。但是最後懶省事寫了乙個差分約束。唉 題解嘛 這題就是普通的差分約束吧?不會差分約束的話,可以看一下這篇部落格,寫的很詳細很周全。注意圖可能不聯通,這個時候要進行多遍spfa。而且注意同一組資料的話不用每次spfa都初始化!如下 include include i...