3924: [Zjoi2015]幻想鄉戰略遊戲
Time Limit: 100 Sec Memory Limit: 256 MBSubmit: 1034 Solved: 478
[Submit][Status][Discuss]
Description
傲嬌少女幽香正在玩一個非常有趣的戰略類遊戲,本來這個遊戲的地圖其實還不算太大,幽香還能管得過來,但是不知道爲什麼現在的網遊廠商把遊戲的地圖越做越大,以至於幽香一眼根本看不過來,更別說和別人打仗了。 在打仗之前,幽香現在面臨一個非常基本的管理問題需要解決。 整個地圖是一個樹結構,一共有n塊空地,這些空地被n-1條帶權邊連接起來,使得每兩個點之間有一條唯一的路徑將它們連接起來。在遊戲中,幽香可能在空地上增加或者減少一些軍隊。同時,幽香可以在一個空地上放置一個補給站。 如果補給站在點u上,並且空地v上有dv個單位的軍隊,那麼幽香每天就要花費dv×dist(u,v)的金錢來補給這些軍隊。由於幽香需要補給所有的軍隊,因此幽香總共就要花費爲Sigma(Dv*dist(u,v),其中1<=V<=N)的代價。其中dist(u,v)表示u個v在樹上的距離(唯一路徑的權和)。 因爲遊戲的規定,幽香只能選擇一個空地作爲補給站。在遊戲的過程中,幽香可能會在某些空地上製造一些軍隊,也可能會減少某些空地上的軍隊,進行了這樣的操作以後,出於經濟上的考慮,幽香往往可以移動他的補給站從而省一些錢。但是由於這個遊戲的地圖是在太大了,幽香無法輕易的進行最優的安排,你能幫幫她嗎? 你可以假定一開始所有空地上都沒有軍隊。
Input
Output
對於幽香的每個操作,輸出操作完成以後,每天的最小花費,也即如果幽香選擇最優的補給點進行補給時的花費。
Sample Input
1 2 1
2 3 1
2 4 1
1 5 1
2 6 1
2 7 1
5 8 1
7 9 1
1 10 1
3 1
2 1
8 1
3 1
4 1
Sample Output
1
4
5
6
HINT
Source
這道題我就呵呵了, 坑我一晚上 + 一下午. 昨天晚上狀態極不好邊想這道題邊去頹課件去了, 導致效率極低. 最後集中想了20min沒想出來選擇看題解, 發現題解清一色的拋結論... 於是自己證了一下, 由於有個sigma寫錯了導致我推了1年... 後面才發現是傻逼結論. 然而一個晚上就這麼在頹廢中過去了QAQ... 不過網上都用的點分治|樹鏈剖分 + 點分治. 昨晚回寢室睡覺時想到了一個純樹鏈剖分的做法. 於是今天下午來實現... 40min寫完沒想到被坑了一下午.
然而這道題是ZJOI2015 Day1 T1. 我選擇go die. 講真t3比t1簡單啊(可能是因爲我沒往性質結論上想...
這道題要求的東西實際上就是帶權重心. 爲什麼呢? 我們設sum[u]爲u子樹的d之和(d詳情請看題目定義). 那麼如果u的兒子v, sum[v] * 2 >= sum[root]的話那麼最優點一定在v的子樹裏. 證明: 我們考慮以u作爲補給點的花費設爲p, 那麼我們會發現若以v作爲補給點的話花費是 p + (sum[root] - sum[v] * 2) * w[u, v]. 因爲走過(u, v)這條邊會使除了v子樹的其他點i的所有花費+d[i] * w(u, v), 那麼就是(sum[root] - sum[v]) * w(u, v). 然後v子樹裏的所有點的花費會 - sum[v] * w(u, v). 加起來就是上式辣. 那麼如果sum[v] * 2 >= sum[root]的話花費就會減少. 而且這樣的v由於sum[v]>=sum[u]/2所以只會最多有一個. 爲什麼不在其他子樹裏呢? 因爲其他子樹譬如v1滿足sum[v1] < sum[root]/2那麼v1子樹裏的點sum更小顯然都滿足, 那麼花費只會增加不會減少. 所以我們只需要順着根往下判斷來找這個最優點就行了(直到兒子都比自己劣那此時就是最優點).
然後我們會發現會tle. 考慮怎麼優化. 首先從根到最優點的路徑是唯一確定的, 並且顯然這條路徑是垂直的鏈, 也就是說深度單增且dfs序單增. 並且從u要走到v的話肯定滿足v是u中sum最大的. 那麼我們用線段樹維護dfs序區間max就可以啦. 每次判斷即可(這個很好理解可以結合代碼seg_query函數). 對於修改直接修改一條鏈的sum即可. 顯然這可以用樹鏈剖分來維護. 那怎麼算答案呢? 我們知道每次移動從u到v花費差距是 (sum[root] - sum[v] * 2) * w[u, v]. 那麼最優點與根的差距就是sum[root] * dis[v](到根距離) - 2 * (sum[v] * w(fa[v], v). 根的答案很好維護, 式子前面一項也很好維護. 第二項發現只跟v有關, 且只有sum一個在變. 那麼樹剖的時候順帶維護一下就好了. 因爲每次修改鏈上都是區間加減, 對於一段dfs序區間的sum[v] * w(fa[v], v)之和(v屬於這段區間)也就是加上了這段區間每個v到各自父親的距離 * 當前改動的值. 詳見代碼(非常easy辣).
然而我老是wa的原因就是之前推導的時候沒考慮邊權, 直接用的dep... 然而樣例恰好邊權都是1, 我...
本來以爲A了可以艹榜的, 發現只能在第二頁(我的ID是lkq的, 我沒有權限號...)... 我還是太naive了. 改天會了zkw線段樹再回來艹一發.
話說爲什麼加了const &和fread讀優還沒有沒加快啊.
而且Claris神犇什麼鬼畜壓行怎麼又寫了樹剖+線段樹還寫了點分治都只有80多行啊
#include<bits/stdc++.h>
using namespace std;
typedef long long lnt;
const int maxn = 2e5 + 5;
lnt ans, all;
int n, Q, num, idx;
int siz[maxn], fa[maxn], son[maxn], seq[maxn], in[maxn], h[maxn], top[maxn], dis[maxn], dep[maxn], par[maxn];
inline const int read() {
register int x = 0, f = 1;
register char ch = getchar();
while (ch < '0' || ch > '9') {if (ch == '-') f = -1; ch = getchar();}
while (ch >= '0' && ch <= '9') x = (x << 3) + (x << 1) + ch - '0', ch = getchar();
return f * x;
}
struct edge {
int nxt, v, w;
}e[maxn << 1];
inline void add(int u, int v, int w) {
e[++ num].v = v, e[num].nxt = h[u], e[num].w = w, h[u] = num;
e[++ num].v = u, e[num].nxt = h[v], e[num].w = w, h[v] = num;
}
void dfs1(int u, int f) {
siz[u] = 1, fa[u] = f;
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == f) continue;
par[v] = e[i].w;
dep[v] = dep[u] + 1;
dis[v] = dis[u] + e[i].w;
dfs1(v, u);
siz[u] += siz[v];
if (siz[son[u]] < siz[v]) son[u] = v;
}
}
void dfs2(int u, int tp) {
top[u] = tp;
in[u] = ++ idx, seq[idx] = u;
if (son[u]) dfs2(son[u], tp);
for (int i = h[u]; i; i = e[i].nxt) {
int v = e[i].v;
if (v == fa[u] || v == son[u]) continue;
dfs2(v, v);
}
}
struct node {
node *ls, *rs;
int mid; lnt sum, flag, cmax, dsm;
inline void pushdown(int lf, int rg) {
if (flag) {
mid = (lf + rg) >> 1;
ls -> sum += flag * ls -> dsm;
rs -> sum += flag * rs -> dsm;
ls -> cmax += flag, rs -> cmax += flag;
ls -> flag += flag, rs -> flag += flag;
flag = 0;
}
}
inline void update() {
sum = ls -> sum + rs -> sum;
cmax = (ls -> cmax > rs -> cmax) ? ls -> cmax : rs -> cmax;
}
}pool[maxn << 1], *root, *tail = pool;
node* build(int lf, int rg) {
node* bt = ++ tail;
if (lf == rg) {
bt -> dsm = par[seq[lf]];
bt -> cmax = bt -> sum = 0;
return bt;
}
int mid = (lf + rg) >> 1;
bt -> ls = build(lf , mid), bt -> rs = build(mid + 1, rg);
bt -> update();
bt -> dsm = bt -> ls -> dsm + bt -> rs -> dsm;
return bt;
}
void modify(node* bt, int lf, int rg, int L, int R, int val) {
if (L <= lf && rg <= R) {
bt -> cmax += val;
bt -> flag += val;
bt -> sum += bt -> dsm * val;
return;
}
bt -> pushdown(lf, rg);
int mid = (lf + rg) >> 1;
if (L <= mid) modify(bt -> ls, lf, mid, L, R, val);
if (R > mid) modify(bt -> rs, mid + 1, rg, L, R, val);
bt -> update();
}
lnt query(node* bt, int lf, int rg, int L, int R) {
if (L <= lf && rg <= R) return bt -> sum;
bt -> pushdown(lf, rg);
int mid = (lf + rg) >> 1; lnt rt = 0;
if (L <= mid) rt += query(bt -> ls, lf, mid, L, R);
if (R > mid) rt += query(bt -> rs, mid + 1, rg, L, R);
bt -> update();
return rt;
}
int queryheart(node* bt, int lf, int rg) {
if (lf == rg) return seq[lf];
bt -> pushdown(lf, rg);
int mid = (lf + rg) >> 1, rt = 0;
if (all <= 2 * bt -> rs -> cmax) rt = queryheart(bt -> rs, mid + 1, rg);
else rt = queryheart(bt -> ls, lf, mid);
bt -> update();
return rt;
}
inline void seg_modify(int u, int val) {
while (u)
modify(root, 1, n, in[top[u]], in[u], val), u = fa[top[u]];
}
inline lnt seg_query() {
lnt rt = 0;
int u = queryheart(root, 1, n), ori = u;
while (u)
rt += query(root, 1, n, in[top[u]], in[u]), u = fa[top[u]];
return all * dis[ori] - 2 * rt;
}
int main() {
n = read(), Q = read();
register int i, u, v, w;
for (i = 1; i < n; ++ i)
u = read(), v = read(), w = read(), add(u, v, w);
dfs1(1, 0), dfs2(1, 1);
root = build(1, n);
for (i = 1; i <= Q; ++ i) {
u = read(), w = read();
seg_modify(u, w);
all += w, ans += 1ll * dis[u] * w;
lnt hh = (ans + seg_query());
printf("%lld\n", hh);
}
return 0;
}