[bzoj2286][Sdoi2011]消耗战

自作多情 提交于 2019-12-03 14:58:15

题目链接

如果对于每个询问跑一次$dp$,那么$dp[i]$为断开$i$这棵子树的最小花费。

这样的复杂度为$O(n*m)$,过于臃肿。

所以我们要对于每次询问降低这次询问的复杂度。

我们可以发现$m$个关键点,最多有$m-1$个$lca$。

简单证明一下,如果有两个点,会有$1$个$lca$点,如果有三个点,则第三个点会和上一个$lca$产生一个$lca$。

所以以这$2*m-1$个点构建一棵树,在这个树上跑$dp$

虚树的构建推荐一个巨巨的博客

  1 #include <bits/stdc++.h>
  2 using namespace std;
  3 typedef long long ll;
  4 const int maxn = 250010;
  5 const ll inf = 2e18 + 10;
  6 struct node {
  7     int s, e, next;
  8     ll w;
  9 }edge[maxn * 2];
 10 int head[maxn], len;
 11 void init() {
 12     memset(head, -1, sizeof(head));
 13     len = 0;
 14 }
 15 void add(int s, int e, ll w) {
 16     edge[len] = { s,e,head[s],w };
 17     head[s] = len++;
 18 }
 19 int fat[maxn], son[maxn], siz[maxn], top[maxn], tid[maxn], dep[maxn], dfx;
 20 ll a[maxn], st[maxn], dp[maxn], Min[maxn], stop;
 21 vector<int>mp[maxn];
 22 void dfs1(int x, int fa, int d) {
 23     siz[x] = 1, fat[x] = fa;
 24     dep[x] = d, son[x] = -1;
 25     for (int i = head[x]; i != -1; i = edge[i].next) {
 26         int y = edge[i].e;
 27         if (y == fa)continue;
 28         Min[y] = min(Min[x], edge[i].w);
 29         dfs1(y, x, d + 1);
 30         siz[x] += siz[y];
 31         if (son[x] == -1 || siz[son[x]] < siz[y])
 32             son[x] = y;
 33     }
 34 }
 35 void dfs2(int x, int c) {
 36     top[x] = c;
 37     tid[x] = ++dfx;
 38     if (son[x] == -1)return;
 39     dfs2(son[x], c);
 40     for (int i = head[x]; i != -1; i = edge[i].next) {
 41         int y = edge[i].e;
 42         if (fat[x] == y || y == son[x])continue;
 43         dfs2(y, y);
 44     }
 45 }
 46 int LCA(int x, int y) {
 47     while (top[x] != top[y]) {
 48         if (dep[top[x]] < dep[top[y]])
 49             swap(x, y);
 50         x = fat[top[x]];
 51     }
 52     if (dep[x] > dep[y])swap(x, y);
 53     return x;
 54 
 55 }
 56 bool cmp(int x, int y) {
 57     return tid[x] < tid[y];
 58 }
 59 void dfs3(int x) {
 60     if (mp[x].size() == 0) {
 61         dp[x] = Min[x];
 62         return;
 63     }
 64     ll sum = 0;
 65     for (int i = 0; i < mp[x].size(); i++) {
 66         int y = mp[x][i];
 67         dfs3(y);
 68         sum += dp[y];
 69     }
 70     mp[x].clear();
 71     dp[x] = min(Min[x], sum);
 72 }
 73 void insert(int x) {
 74     if (stop == 1) {
 75         st[++stop] = x;
 76         return;
 77     }
 78     int lca = LCA(x, st[stop]);
 79     if (lca == st[stop])
 80         return;
 81     while (stop > 1 && tid[st[stop - 1]] >= tid[lca])
 82         mp[st[stop - 1]].push_back(st[stop]), stop--;
 83     if (lca != st[stop])
 84         mp[lca].push_back(st[stop]), st[stop] = lca;
 85     st[++stop] = x;
 86 }
 87 int main() {
 88     init();
 89     int n, m, t, x, y, z;
 90     scanf("%d", &n);
 91     for (int i = 1; i < n; i++) {
 92         scanf("%d%d%d", &x, &y, &z);
 93         add(x, y, z);
 94         add(y, x, z);
 95     }
 96     Min[1] = inf;
 97     dfs1(1, 0, 1);
 98     dfs2(1, 1);
 99     scanf("%d", &m);
100     while (m--) {
101         scanf("%d", &t);
102         for (int i = 1; i <= t; i++)
103             scanf("%d", &a[i]);
104         sort(a + 1, a + 1 + t, cmp);
105         st[stop = 1] = 1;
106         for (int i = 1; i <= t; i++)
107             insert(a[i]);
108         while (stop > 1)
109             mp[st[stop - 1]].push_back(st[stop]), stop--;
110         dfs3(1);
111         printf("%lld\n", dp[1]);
112     }
113 }

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!