傳送門
題目意思很簡單,意思是說給你一些區間和,要你判斷這些區間和是否合法。
開始只想到了差分約束的方法,就是搞成字首和的形式 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...