[ZJOI2015] township fantasy strategy game - dynamic point divide and conquer

First consider how to operate without repair.

graph (2).png

It found that in the absence of repair, we can use a change root \ (dp \) to solve.

Then the situation with modifications to how to do it?

Each modification re \ (dp \) again not on the list (fog.

Well, let's knock on a \ (O (N ^ 2) \) The \ (dp \) .

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
inline ll ty() {
  char ch = getchar(); ll x = 0, f = 1;
  while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
  while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
  return x * f;
}

const int _ = 1e5 + 10;
const ll INF = 1e18;
int tot, head[_], to[_ << 1], nxt[_ << 1], edge[_ << 1];

void adde(int x, int y, int z) {
  to[++tot] = y;
  edge[tot] = z;
  nxt[tot] = head[x];
  head[x] = tot;
}

int N, M;
ll val[_], g[_], f[_], sum, ans;

void dfs(int x, int fa, ll d) {
  f[1] += d * val[x];
  g[x] = val[x];
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (y == fa) continue;
    dfs(y, x, d + z);
    g[x] += g[y];
  }
}

void dp(int x, int fa) {
  ans = min(ans, f[x]);
  for (int i = head[x]; i; i = nxt[i]) {
    int y = to[i], z = edge[i];
    if (y == fa) continue;
    f[y] = f[x] - g[y] * z + (sum - g[y]) * z;
    dp(y, x);
  }
}

void work() {
  f[1] = 0, ans = INF;
  dfs(1, 0, 0);  //先以1为根,求出答案,然后进行换根dp
  dp(1, 0);
  printf("%lld\n", ans);
}

int main() {
#ifndef ONLINE_JUDGE
  freopen("fantasy.in", "r", stdin);
  freopen("fantasy.out", "w", stdout);
#endif
  N = ty(), M = ty();
  for (int i = 1; i < N; ++i) {
    int x = ty(), y = ty(), z = ty();
    adde(x, y, z);
    adde(y, x, z);
  }
  while (M--) {
    int x = ty(), y = ty();
    val[x] += y, sum += y;
    work();  // 每修改一次,就重新dp一次2333
  }
  return 0;
}

Then is this grand occasion:

"Nothing, Kaka often pass by."@mzx

Because I was too dishes, so do not put \ (O (N ^ 2) \) card into a card often \ (O (N \ log N ) \) capabilities.

Consider this first \ (dp \) What is the nature of consideration from a point \ (x \) moved to the point \ (the y-\) , then the change in the value of the cost of light is
\ [dis_ {x, y} \ times ( sum - 2 \ times g_y) \
] we found that when \ (sum-2 \ times g_y \) is negative when the answer will be reduced, and this can prove every single point, so in fact, we from \ (root \) began to depart, each to \ (sum - 2 \ times g_y \) to go negative in that direction, until the walk, you get the optimal solution.

Here's what a dynamic point of partition

Found mainly after each modification, are required from \ (root \) node to re-start looking for the optimal solution, or in the worst case will reach \ (O (N ^ 2) \) complexity, so consider how to optimize this process. At this point, it should be something to point the sub-tree.

Suppose we can use trees dotted seek answers, because only need to access the dotted tree \ (\ log \) points, so a modification of the complexity dropped to \ (\ log \) level.

At this point, the correct approach seems to be gradually emerged:

Node is currently located is assumed to be \ (X \) , then we enumerate \ (X \) edges of the original tree, assuming the other end of this edge is \ (Y \) , followed by \ (O (\ log N ) \) is calculated if the complexity of violence to move \ (Y \) , how much it cost, if the cost is reduced, then the corresponding sub-tree block jump point, then solving the corresponding block which, if not there is a side, so sure is the optimal solution.

Now think about what things need to maintain this approach:

  1. Weight of each block and the point \ (sum_x \) (dotted tree \ (O (\ log N) \) modified)
  2. \ (dist_x \) : \ (X \) all the points in the sub-tree corresponding to point link block skip \ (X \) spending
  3. \ (, sub_x \) : \ (X \) all the points in the sub-tree corresponding to point link block skip \ (X \) at the dotted tree father \ (fa_x \) spending

Then continue to query and modify the rampaging father just fine.

#include <bits/stdc++.h>
using namespace std;

inline int ty() {
    char ch = getchar(); int x = 0, f = 1;
    while (ch < '0' || ch > '9') { if (ch == '-') f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}

typedef long long ll;
const int _ = 1e5 + 10;
const int INF = 0x3f3f3f3f;
int N, M;
int tot, head[_], to[_ << 1], nxt[_ << 1], edge[_ << 1];

void adde(int x, int y, int z) {
    to[++tot] = y;
    edge[tot] = z;
    nxt[tot] = head[x];
    head[x] = tot;
}

namespace lca {
int fa[_], son[_], dep[_], dis[_], siz[_], top[_];
void dfs1(int x, int f) {
    siz[x] = 1;
    int maxx = 0;
    for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i], z = edge[i];
        if (y == f) continue;
        fa[y] = x, dep[y] = dep[x] + 1, dis[y] = dis[x] + z;
        dfs1(y, x);
        siz[x] += siz[y];
        if (siz[y] > maxx) maxx = siz[y], son[x] = y;
    }
}
void dfs2(int x, int topf) {
    top[x] = topf;
    if (!son[x]) return;
    dfs2(son[x], topf);
    for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
    }
}
void init() {
    dep[1] = 1, dfs1(1, 0);
    dfs2(1, 1);
}
int query(int x, int y) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        x = fa[top[x]];
    }
    return dep[x] < dep[y] ? x : y;
}
int dist(int x, int y) { return dis[x] + dis[y] - 2 * dis[query(x, y)]; }
} // namespace lca

int mxsiz, totsiz, root, siz[_], vis[_], fa[_];

void getroot(int x, int f) {
    siz[x] = 1;
    int maxx = 0;
    for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y] || y == f) continue;
        getroot(y, x);
        siz[x] += siz[y];
        maxx = max(maxx, siz[y]);
    }
    maxx = max(maxx, totsiz - siz[x]);
    if (maxx < mxsiz) mxsiz = maxx, root = x;
}

typedef pair<int, int> PII;
vector<PII> E[_];

void divide(int x) {
    vis[x] = true;
    int nowsiz = totsiz;
    for (int i = head[x]; i; i = nxt[i]) {
        int y = to[i];
        if (vis[y]) continue;
        mxsiz = INF, root = 0;
        totsiz = siz[y] > siz[x] ? nowsiz - siz[x] : siz[y];
        getroot(y, 0);
        E[x].push_back(make_pair(y, root));
        fa[root] = x;
        divide(root);
    }
}

ll sum[_];  // 点分树中以x为根的子树的权值和
ll dist[_]; // 点分树中以x为根的子树全部到x的花费
ll sub[_];  // 点分树中以x为根的子树全部到x在点分树上的父亲的花费

void modify(int x, int y) {
    sum[x] += y;
    for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dist(x, fa[i]);
        sum[fa[i]] += y;
        dist[fa[i]] += 1ll * y * len;
        sub[i] += 1ll * y * len;
    }
}

// 用一个log的代价直接计算以x为供应站时的话费
ll calc(int x) {
    ll ret = dist[x];
    for (int i = x; fa[i]; i = fa[i]) {
        int len = lca::dist(x, fa[i]);
        ret += 1ll * len * (sum[fa[i]] - sum[i]);
        ret += dist[fa[i]] - sub[i];
    }
    return ret;
}

ll query(int x) {
    // printf("%d\n", x);
    ll cur = calc(x);
    for (auto p : E[x]) {
        int y = p.first, rt = p.second;
        // printf("%d %d\n", y, rt);
        if (calc(y) < cur) return query(rt);
    }
    return cur;
}

int main() {
#ifndef ONLINE_JUDGE
    freopen("fantasy.in", "r", stdin);
    freopen("fantasy.out", "w", stdout);
#endif
    N = ty(), M = ty();
    for (int i = 1; i < N; ++i) {
        int x = ty(), y = ty(), z = ty();
        adde(x, y, z);
        adde(y, x, z);
    }
    lca::init();

    mxsiz = INF, totsiz = N, root = 0;
    getroot(1, 0);
    int RT = root;
    divide(root);

    while (M--) {
        // printf("!%d\n", M);
        int x = ty(), y = ty();
        modify(x, y);
        printf("%lld\n", query(RT));
    }
    return 0;
}

Guess you like

Origin www.cnblogs.com/newbielyx/p/12129692.html