洛谷 P5024 保卫王国

Z国有n座城市,n−1条双向道路,每条双向道路连接两座城市,且任意两座城市 都能通过若干条道路相互到达。

Z国的国防部长小Z要在城市中驻扎军队。驻扎军队需要满足如下几个条件:

  • 一座城市可以驻扎一支军队,也可以不驻扎军队。
  • 由道路直接连接的两座城市中至少要有一座城市驻扎军队。
  • 在城市里驻扎军队会产生花费,在编号为i的城市中驻扎军队的花费是\(p_i\)

小 Z 很快就规划出了一种驻扎军队的方案,使总花费最小。但是国王又给小Z提出了m个要求,每个要求规定了其中两座城市是否驻扎军队。小Z需要针对每个要求逐一给出回答。具体而言,如果国王提出的第j个要求能够满足上述驻扎条件(不需要考虑第j个要求之外的其它要求),则需要给出在此要求前提下驻扎军队的最小开销。如果 国王提出的第j个要求无法满足,则需要输出-1\((1\le j\le m)\)。现在请你来帮助小Z。

真是一道noip的毒瘤题

最小权覆盖集=点权和-最大权独立集

每次询问选或不选相当于给每个点赋无穷小或无穷大,然后就可以ddp了,好了做完了

但是这个题没有修改,所以我们考虑倍增

先预处理出来\(f_{u,0/1}\)表示u不选或选时以其为根子树的答案,\(g_{u,0/1}\)表示u不选或选时整棵树去掉以u为根的子树的答案

这两个都可以树上dp出来

然后我们维护一个倍增数组\(dp_{u,i,0/1,0/1}\)表示u不选或选和u的\(2^i\)祖先不选或选时,以u的\(2^i\)祖先为根的子树去掉以u为根的子树的答案

这个也可以顺着树上dp的思路倍增出来

然后根据倍增往上跳就可以了,顺便统计一下答案

当然,只有在a,b都不能驻扎军队并且a和b有边相连的情况下才会无解

#include <iostream>
#include <cstdio>
#include <algorithm>
#include <vector>
#define LL long long
const int N = 1e5;
const LL INF = 1e15;
using namespace std;
int n,m,p[N + 5];
int dep[N + 5],fa[N + 5][25];
LL dp[N + 5][25][2][2],f[N + 5][2],g[N + 5][2];
char ch[5];
vector <int> d[N + 5];
void dfs1(int u,int F)
{
    vector <int>::iterator it;
    fa[u][0] = F;
    f[u][1] = p[u];
    dep[u] = dep[F] + 1;
    for (it = d[u].begin();it != d[u].end();it++)
    {
        int v = (*it);
        if (v == F)
            continue;
        dfs1(v,u);
        f[u][1] += min(f[v][0],f[v][1]);
        f[u][0] += f[v][1];
    }
}
void dfs2(int u,int F)
{
    vector <int>::iterator it;
    for (it = d[u].begin();it != d[u].end();it++)
    {
        int v = (*it);
        if (v == F)
            continue;
        g[v][0] = g[u][1] + f[u][1] - min(f[v][1],f[v][0]);
        g[v][1] = min(g[u][0] + f[u][0] - f[v][1],g[v][0]);
        dfs2(v,u);
    }
}
LL solve(int a,int x,int b,int y)
{
    if (dep[a] < dep[b])
        swap(a,b),swap(x,y);
    LL ta[2] = {INF,INF},tb[2] = {INF,INF},na[2],nb[2];
    ta[x] = f[a][x];
    tb[y] = f[b][y];
    for (int i = 18;i >= 0;i--)
        if (dep[fa[a][i]] >= dep[b])
        {
            na[0] = na[1] = INF;
            for (int j = 0;j <= 1;j++)
                for (int k = 0;k <= 1;k++)
                    na[j] = min(na[j],ta[k] + dp[a][i][k][j]);
            ta[1] = na[1];
            ta[0] = na[0];
            a = fa[a][i];
        }
    if (a == b)
        return ta[y] + g[b][y];
    for (int i = 18;i >= 0;i--)
        if (fa[a][i] != fa[b][i])
        {
            na[0] = na[1] = nb[0] = nb[1] = INF;
            for (int j = 0;j <= 1;j++)
                for (int k = 0;k <= 1;k++)
                {
                    na[j] = min(na[j],ta[k] + dp[a][i][k][j]);
                    nb[j] = min(nb[j],tb[k] + dp[b][i][k][j]);
                }
            ta[1] = na[1];
            ta[0] = na[0];
            tb[1] = nb[1];
            tb[0] = nb[0];
            a = fa[a][i];
            b = fa[b][i];
        }
    int lca = fa[a][0];
    return min(f[lca][0] - f[a][1] - f[b][1] + ta[1] + tb[1] + g[lca][0],f[lca][1] - min(f[a][0],f[a][1]) - min(f[b][0],f[b][1]) + min(ta[0],ta[1]) + min(tb[0],tb[1]) + g[lca][1]);
}
int main()
{
    scanf("%d%d",&n,&m);
    scanf("%s",ch + 1);
    for (int i = 1;i <= n;i++)
        scanf("%d",&p[i]);
    int u,v;
    for (int i = 1;i < n;i++)
    {
        scanf("%d%d",&u,&v);
        d[u].push_back(v);
        d[v].push_back(u);
    }
    for (int i = 1;i <= n;i++)
        sort(d[i].begin(),d[i].end());
    dfs1(1,0);
    dfs2(1,0);
    for (int i = 1;i <= n;i++)
    {
        dp[i][0][0][0] = INF;
        dp[i][0][1][0] = f[fa[i][0]][0] - f[i][1];
        dp[i][0][1][1] = f[fa[i][0]][1] - min(f[i][0],f[i][1]);
        dp[i][0][0][1] = f[fa[i][0]][1] - min(f[i][0],f[i][1]);
    }
    for (int i = 1;i <= 18;i++)
        for (int j = 1;j <= n;j++)
        {
            fa[j][i] = fa[fa[j][i - 1]][i - 1];
            for (int x = 0;x <= 1;x++)
                for (int y = 0;y <= 1;y++)
                {
                    dp[j][i][x][y] = INF;
                    for (int z = 0;z <= 1;z++)
                        dp[j][i][x][y] = min(dp[j][i][x][y],dp[j][i - 1][x][z] + dp[fa[j][i - 1]][i - 1][z][y]);
                }
        }
    int a,x,b,y;
    for (int i = 1;i <= m;i++)
    {
        scanf("%d%d%d%d",&a,&x,&b,&y);
        if (x == 0 && y == 0 && *lower_bound(d[a].begin(),d[a].end(),b) == b)
            printf("-1\n");
        else
            printf("%lld\n",solve(a,x,b,y));
    }
    return 0;
}
posted @ 2020-06-08 21:20  eee_hoho  阅读(86)  评论(0编辑  收藏  举报