Luogu2495[SDOI2011]消耗戰
阿新 • • 發佈:2019-02-10
sigma 記錄 can sdoi2011 long long 模型 整數 from define 偵查部門還發現,敵軍有一臺神秘機器。即使我軍切斷所有能源之後,他們也可以用那臺機器。機器產生的效果不僅僅會修復所有我軍炸毀的橋梁,而且會重新隨機資源分布(但可以保證的是,資源不會分布到\(1\)
對於\(100\%\)的數據,\(2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1\)
題目描述
在一場戰爭中,戰場由\(n\)島嶼和\(n-1\)個橋梁組成,保證每兩個島嶼間有且僅有一條路徑可達。現在,我軍已經偵查到敵軍的總部在編號為\(1\)的島嶼,而且他們已經沒有足夠多的能源維系戰鬥,我軍勝利在望。已知在其他\(k\)個島嶼上有豐富能源,為了防止敵軍獲取能源,我軍的任務是炸毀一些橋梁,使得敵軍不能到達任何能源豐富的島嶼。由於不同橋梁的材質和結構不同,所以炸毀不同的橋梁有不同的代價,我軍希望在滿足目標的同時使得總代價最小。
偵查部門還發現,敵軍有一臺神秘機器。即使我軍切斷所有能源之後,他們也可以用那臺機器。機器產生的效果不僅僅會修復所有我軍炸毀的橋梁,而且會重新隨機資源分布(但可以保證的是,資源不會分布到\(1\) 號島嶼上)。不過偵查部門還發現了這臺機器只能夠使用\(m\)次,所以我們只需要把每次任務完成即可。
輸入格式:
第一行一個整數\(n\),代表島嶼數量。
接下來\(n-1\)行,每行三個整數\(u\),\(v\),\(w\),代表\(u\)號島嶼和\(v\)號島嶼由一條代價為\(c\)的橋梁直接相連,保證\(1<=u,v<=n\)且\(1<=c<=100000\)。
第\(n+1\)行,一個整數\(m\),代表敵方機器能使用的次數。
接下來\(m\)行,每行一個整數\(ki\),代表第\(i\)次後,有\(ki\)個島嶼資源豐富,接下來\(k\)個整數\(h1,h2,…hk\),表示資源豐富島嶼的編號。
輸出格式:
輸出有\(m\)行,分別代表每次任務的最小代價。
【數據規模和約定】
對於\(10\%\)的數據,\(2<=n<=10,1<=m<=5,1<=ki<=n-1\)
對於\(20\%\)的數據,\(2<=n<=100,1<=m<=100,1<=ki<=min(10,n-1)\)
對於\(40\%\)的數據,\(2<=n<=1000,m>=1,sigma(ki)<=500000,1<=ki<=min(15,n-1)\)
對於\(100\%\)的數據,\(2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1\)
題目中\(k\)的總數有\(500000\),顯然虛樹優化樹形\(DP\).因為是第一次寫虛樹所以出了不少\(bug\),這裏來總結一下怎麽寫虛樹.
首先,你要能建立出樹形\(DP\)的樸素模型
在此基礎上,由於詢問點數和有限,所以我們並不需要對每次詢問都\(O(N)\)\(DP\)回答.這裏我們進行一次\(DFS\)的預處理,只抽出有效信息的一顆濃縮的樹.那麽關鍵就在怎麽把它抽出來了.
對這個題,我們可以記錄一個根節點到每個節點的最窄部分.然後對詢問點按照\(dfs\)序排序.排序過後,對每兩個\(dfs\)序相鄰節點求\(lca\)扔進要用的點裏.(可以口胡得到:這樣一定涵蓋所有必要的\(LCA\))再按\(dfs\)序排一次序,然後就可以愉快地建樹\(DP\)啦.
Code:
#include <bits/stdc++.h>
#define int long long
#define N 250010
using namespace std;
int n, m, k, u, v, w, cnt, val[N], head[N];
int ss[N << 1], sta[N << 1], deep[N], ff[N][22], done[N];
struct edge {
int nxt, to, w;
}e[N << 1];
void add_edge (int from, int to, int val) {
e[++cnt].nxt = head[from];
e[cnt].to = to;
e[cnt].w = val;
head[from] = cnt;
}
int dfn[N], low[N], _dfn = 0;
void pre (int u, int fa) {
dfn[u] = ++_dfn;
deep[u] = deep[fa] + 1;
ff[u][0] = fa;
for (int i = 1; (1 << i) <= deep[u]; ++i) {
ff[u][i] = ff[ff[u][i - 1]][i - 1];
}
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v != fa) {
val[v] = min (val[u], e[i].w);
pre (v, u);
// printf ("val[%lld] = min (%lld, %lld)\n", v, val[u], e[i].w);
}
}
low[u] = _dfn;
}
bool cmp (const int &lhs, const int &rhs) {
return dfn[lhs] < dfn[rhs];
}
int lca (int u, int v) {
// printf ("lca (%lld, %lld) = ", u, v);
if (deep[u] < deep[v]) swap (u, v);
for (int i = 20; i >= 0; --i) {
if (deep[ff[u][i]] >= deep[v]) {
u = ff[u][i];
}
}
if (u == v) return u;
for (int i = 20; i >= 0; --i) {
if (ff[u][i] != ff[v][i]) {
u = ff[u][i];
v = ff[v][i];
}
}
//printf ("%lld\n", ff[u][0]);
return ff[u][0];
}
int get_ans (int u, int fa) {
if (done [u]) return val[u];
int res = 0;
for (int i = head[u]; i; i = e[i].nxt) {
int v = e[i].to;
if (v != fa) {
res += get_ans (v, u);
}
}
return min (res, val[u]);
}
signed main () {
//freopen ("2495.in", "r", stdin);
scanf ("%lld", &n);
for (int i = 1; i < n; ++i) {
scanf ("%lld %lld %lld", &u, &v, &w);
add_edge (u, v, w);
add_edge (v, u, w);
}
memset (val, 0x3f, sizeof (val));
pre (1, 0);
/*
for (int i = 1; i <= n; ++i) {
printf ("node = %lld, val = %lld, low = %lld, dfn = %lld\n", i, val[i], low[i], dfn[i]);
} */
memset (head, 0, sizeof (head));
scanf ("%lld", &m);
for (int i = 1; i <= m; ++i) {
// printf ("ask = %lld\n", i);
scanf ("%lld", &k);
for (int j = 1; j <= k; ++j) {
scanf ("%lld", &ss[j]);
done[ss[j]] = true;
}
sort (ss + 1, ss + 1 + k, cmp);
for (int j = k; j > 1; --j) {
ss[++k] = lca (ss[j], ss[j - 1]);
}
ss[++k] = 1;
sort (ss + 1, ss + 1 + k, cmp);
k = unique (ss + 1, ss + 1 + k) - ss - 1;
int top = 0; cnt = 0;
for (int j = 1; j <= k; ++j) {
while (top && low[sta[top]] < dfn[ss[j]]) --top;
add_edge (sta[top], ss[j], val[j]);
add_edge (ss[j], sta[top], val[j]);
sta[++top] = ss[j];
}
get_ans (1, 0);
//print_tree (1, 0);
printf ("%lld\n", get_ans (1, 0));
for (int j = 1; j <= k; ++j) {
//printf ("dp[%lld] = %lld\n", ss[j], dp[ss[j]]);
done[ss[j]] = false, head[ss[j]] = 0;
}
}
}
Luogu2495[SDOI2011]消耗戰