time limit: 20 sec
memory limit: 162 mb
submit: 1098
solved: 539 [
submit][
status][
discuss]
ps國是乙個擁有諸多城市的大國,國王louis為城市的交通建設可謂絞盡腦汁。louis可以在某些城市之間修建道路,在不同的城市之間修建道路需要不同的花費。louis希望建造最少的道路使得國內所有的城市連通。但是由於某些因素,城市之間修建道路需要的花費會隨著時間而改變,louis會不斷得到某道路的修建代價改變的訊息,他希望每得到一條訊息後能立即知道使城市連通的最小花費總和, louis決定求助於你來完成這個任務。
檔案第一行包含三個整數n,m,q,分別表示城市的數目,可以修建的道路個數,及收到的訊息個數。 接下來m行,第i+1行有三個用空格隔開的整數xi,yi,zi(1≤xi,yi≤n, 0≤zi≤50000000),表示在城市xi與城市yi之間修建道路的代價為zi。接下來q行,每行包含兩個數k,d,表示輸入的第k個道路的修建代價修改為d(即將zk修改為d)。
輸出包含q行,第i行輸出得知前i條訊息後使城市連通的最小花費總和。
5 5 3
1 2 1
2 3 2
3 4 3
4 5 4
5 1 5
1 61 1
5 314109
【資料規模】 對於20%的資料, n≤1000,m≤6000,q≤6000。 有20%的資料,n≤1000,m≤50000,q≤8000,修改後的代價不會比之前的代價低。 對於100%的資料, n≤20000,m≤50000,q≤50000。 [
submit][
status][
discuss]
cdq分治,,啊。。想也想不到
傳送門這裡的ppt有很詳細的介紹了
所以就補充一點筆記吧。。
每次修改只是改了一條邊的權值,因此,修改之後的mst有著極大的相似性,藉此分治
定義過程solve(l,r)為處理(l,r)的詢問,然後將圖修改為做完(l,r)修改之後的狀態
為了很好地實現修改和詢問操作,要明確一些事情,,
一開始得到圖g,乙個點集v和乙個邊集e,要做mst
假如強制e的某個子集s中所有邊不選,然後做一次mst,假設得到一棵樹t
那麼e - t中不屬於s的所有邊,無論s中的所有邊邊權如何改變,永遠都不會被選
因為我們已經能找到更優的東西,且s的改變無法影響e - t中不屬於s的邊
再強制s中所有邊優先考慮,做一次mst,得到一棵樹t'
此時t' - s的邊無論s中所有邊邊權如何改變,永遠都會存在於mst中
因為這些邊是優先s的情況下選擇的,s無論如何改變,只能更劣,並不影響這些邊的選擇
現在,定義s = ,
那麼每次,我們執行操作1,刪掉那些永遠不會被選的邊,記這個操作為reduction
然後對於剩下的邊,執行操作2,強制加入所有必選邊,或是說縮點,記這個操作為contraction
那麼每次,reduction後,剩下的邊的數量最壞是|s| + n - 1,就是g - s中的邊能生成一棵樹
而contraction後,剩下的點的數量最壞是|s|,也就是|s|中的邊剛好無環
整個過程放在分治裡,每次|s|會縮小一半,因而操作1剩下的邊數最壞約是3*s,因為n和上一層分治乙個數量級
3已經可以看做是常數了,那麼我們只要在分治的時候reduction,contraction,將新圖傳遞給子孫解決
當l == r,剩下2~3個點,一些邊,此處先修改,然後做乙個mst,詢問就解決了,
返回的時候定義操作recover,能抵消reduction-contraction的操作就行了,
顯然recover的複雜度和reduction-contraction乙個數量級
縮點苟蒻是用安軼合併的並查集,然後solve(l,mid)返回時對邊集重新排序下
因此總的複雜度是o(nlog^n)
#include#include#include#include#include#include#include#includeusing namespace std;
const int maxn = 2e4 + 20;
const int maxm = 5e4 + 50;
typedef long long ll;
struct e
e(int x,int y,int w): x(x),y(y),w(w){}
}edgs[maxm];
struct data
data(int x,int y,int l): x(x),y(y),l(l){}
}s[maxm];
int n,m,q,cnt,tp,l[maxn],mark[maxm],po[maxm],d[maxm],fa[maxn],f[maxn];
ll ans[maxm];
bool cmp(const int &x,const int &y)
int getf(int x)
int getfa(int x)
void merge(int x,int y)
void reset_fa(vector v)
}void reduction(vector &v,vector &g)
}void contraction(vector &g,vector &t,ll &tot)
for (int i = 0; i < g.size(); i++)
e e = edgs[g[i]];
int x = getf(e.x),y = getf(e.y);
int fx = getfa(x),fy = getfa(y);
if (fx == fy)
tot += 1ll*e.w;
merge(x,y); fa[fx] = fy; }}
void recover(int cur)
}void solve(int l,int r,vector &v,ll tot)
ans[l] = tot;
recover(cur);
return;
} ++cnt; int mid = (l + r) >> 1;
for (int i = l; i <= r; i++) mark[po[i]] = cnt;
vector g,t; g.clear(); t.clear();
int cur = tp;
reduction(v,g);
contraction(g,t,tot);
solve(l,mid,t,tot);
sort(t.begin(),t.end(),cmp);
solve(mid+1,r,t,tot);
recover(cur);
}int main()
for (int i = 1; i <= n; i++) f[i] = i,l[i] = 1;
for (int i = 1; i <= q; i++) scanf("%d%d",&po[i],&d[i]);
vector v; v.clear();
for (int i = 1; i <= m; i++) v.push_back(i);
sort(v.begin(),v.end(),cmp);
solve(1,q,v,0);
for (int i = 1; i <= q; i++) printf("%lld\n",ans[i]);
return 0;
}
BZOJ2001 HNOI2010 城市建設
題目大意 動態最小生成樹,可以離線,每次修改後回答,點數20000,邊和修改都是50000。顧昱洲是真的神 顧昱洲 一類分治演算法 講的很妙,大致的幾個注意點在 裡面也有提到。include include include include include include define ll long...
HNOI2001 產品加工
題目描述 某加工廠有a b兩台機器,來加工的產品可以由其中任何一台機器完成,或者兩台機器共同完成。由於受到機器效能和產品特性的限制,不同的機器加工同一產品所需的時間會不同,若同時由兩台機器共同進行加工,所完成任務又會不同。某一天,加工廠接到n個產品加工的任務,每個任務的工作量不盡一樣。你的任務就是 ...
HNOI2001 產品加工
hnoi2001 產品加工 有機器甲乙二者,加工產品者也.今有 n 個產品,加工於甲需耗時間 t 加工於乙需耗時間 t 二者同時為其加工需耗時間 t 若 t 為 0 則表示無法用此方式加工 機器者,專一者也,一時只加工一產品也.試問加工完畢最小時間有幾何.挺神奇的一道題.一開始考慮開三個陣列 f 3...