洛谷p2294 [hnoi2005]狡猾的商人
前言大概一定是我太蒻了,卡在這道題的差分約束做法上卡了幾個小時qaq
在摸差分約束做法前,得出了類似於這位dalao題解的思路,可惜因為沒想到優先佇列而選擇了\(vector\),所以沒有實現(在看了題解之後獨自編出來了,會具體講解)
做法(對優先佇列那種神奇做法不感興趣的可以直接跳過看差分約束做法)
優先佇列
還是算有點貪心+模擬的思路吧?因為這種做法好像沒名字(傳說中的玄學孤兒演算法qwq)
先來分析一波兩個樣例(直接見圖即可):
所以,我們可以得出一種解決方法:
先將輸入的區間按規則排序,然後依次比較前後兩個區間,遇到左端點相等的就推出另乙個式子壓入待處理判斷的陣列中,如果有兩個式子相矛盾就直接輸出\(false\),否則最後輸出\(true\)
排序規則:左端點相等則右端點小的在前,否則按左端點小的排在前
這裡我們需要思考一下,因為是前後兩個區間比對,所以我們每壓入乙個新的式子都要進行如上排序。那怎麼實現?直接\(sort\)肯定是會超時的
在看了最上面那篇題解後,恍然大悟:直接用過載的優先佇列啊!
至於時間複雜度的證明,我也不會,但是優先佇列確實比\(sort\)跑得快
放上**:
#include using namespace std;
int t,n,m;
struct node
} a;
priority_queueq;
int main()
node fir=q.top();
q.pop();
// cout差分約束系統
不了解差分約束系統的珂見這篇部落格啊qwq 無恥
我們首先需要確定的是這題跑最短路還是最長路
我個人偏向於跑最長路(因為跑最短路似乎也能a?)
為什麼是最長路?因為在差分約束系統中跑最長路得到的是最小解(不懂的就上面的鏈結吧ovo),且如果最小解都滿足了那大一點的解肯定也滿足
那本題的約束條件是什麼?直接是輸入中的\(t-s=v\)嗎?
當然不是!轉換一下思路,從\(s\)到\(t\)的總收入可以用字首和表示:\(sum[i]\)表示\(i\)月之前的所有月收入的總和
那約束條件則是\(sum[t]-sum[s-1]=v\)!
再轉換上面那個式子,就得到如何建邊:
\(sum[t]-sum[s-1]≥v\),所以從\(t\)到\(s-1\)連一條權值為\(v\)的邊
\(sum[t]-sum[s-1]≤v\),所以從\(s-1\)到\(t\)連一條權值為\(-v\)的邊
然後就是正常的跑差分約束系統的最長路板子了(這裡採用全部入隊的方式,而不是使用超級源點)
**如下:
#include using namespace std;
int t,n,m,u,v,w,tot;
int dis[250010],vis[250010],cnt[250010],head[250010];
struct node e[250010];
inline void add(int u,int v,int w)
inline bool spfa()
while(!q.empty()) {
int x=q.front();
q.pop();
vis[x]=0;
for(register int i=head[x];i;i=e[i].net) {
int v=e[i].to;
if(dis[v]然後,感謝一些同桌對我程式的修改
狡猾的商人 題解
題目描述 刁奼接到乙個任務,為稅務部門調查一位商人的賬本,看看賬本是不是偽造的。賬本上記錄了n個月以來的收入情況,其中第i 個月的收入額為ai i 1,2,3.n 1,n 當 ai大於0時表示這個月盈利ai 元,當 ai小於0時表示這個月虧損ai 元。所謂一段時間內的總收入,就是這段時間內每個月的收...
bzoj1202 狡猾的商人
如果這個賬本是真的話,那麼對於乙個s,t,在圖上,兩個點之間任意一條路徑的長度都必須相等,不然這個賬本就不是真的。用並查集在維護這個資訊,也就是字首和,字首和就是前i個月收入的錢,那麼對於任意一行資料 s t v 都有 sum s sum t v 然後在並查集的時候,查詢父親節點的時候,將sum值累...
HNOI2005 狡猾的商人
刁奼接到乙個任務,為稅務部門調查一位商人的賬本,看看賬本是不是偽造的。賬本上記錄了n個月以來的收入情況,其中第i 個月的收入額為ai i 1,2,3 n 1,n 當 ai大於0時表示這個月盈利ai 元,當 ai小於0時表示這個月虧損ai 元。所謂一段時間內的總收入,就是這段時間內每個月的收入額的總和...