ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

P5024 [NOIP2018 提高组] 保卫王国

2022-08-15 20:01:35  阅读:152  来源: 互联网

标签:std NOIP2018 fa reads lu include P5024 nu 保卫


传送门


思路

如果没有强制,那就是一个简单的树形DP,我们用 \(f[i][0/1]\) 表示 \(i\) 的子树内,\(i\) 选或不选的最小代价;用 \(g[i][0/1]\) 表示整个树减去 \(i\) 的子树,\(i\) 选或不选单最小代价。这类似于换根DP

有了强制,说明我们的DP有一些状态不可取,虽然我们不能退回去再做一次DP,但我们可以记录一些信息,达到 \(\log n\) 来快速计算

我们设 \(h[i][j][0/1][0/1]\) 代表 \(i\) 结点和向上跳 \(2^j\) 个结点到某个祖先,然后 \(i\) 选或不选,祖先选或不选;里面存的是不包含 \(f[i]\) 和 \(g[fa[i][j]]\) 的最小代价(也就是整棵树,除去“ \(i\) 的子树”和“不含 \(fa[i][j]\) 的子树的树”)

每次强制要求 \(u,v\),我们就用倍增求 \(\text{LCA}(u,v)\) 的同时,将路径上的 \(h\) 合并:

  1. 当 \(v\) 是 \(u\) 的祖先时,那么答案就是 \(h[u][v][a][b] + f[u][a]+g[v][b]\)(\(a,b\) 表示 \(u,v\) 强制选或不选,这里的 \(h\) 是倍增合并后的)

  2. 如果不是祖孙关系,那么就一路合并到 \(\text{LCA}(u,v)\) 的儿子,最后分类讨论两个儿子的状态,取最小值


代码

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<queue>
#include<map>
#include<set>
#include<bitset>
#define LL long long
#define FOR(i, x, y) for(int i = (x); i <= (y); i++)
#define ROF(i, x, y) for(int i = (x); i >= (y); i--)
#define PFOR(i, x) for(int i = he[x]; i; i = r[i].nxt)
inline int reads()
{
    int sign = 1, re = 0; char c = getchar();
    while(c < '0' || c > '9'){if(c == '-') sign = -1; c = getchar();}
    while('0' <= c && c <= '9'){re = re * 10 + (c - '0'); c = getchar();}
    return sign * re;
}
const LL INF = 1e18;
int n, m, a[100005]; char _[2];
struct Node
{
    int to, nxt;
}r[200005]; int he[100005];
inline void Edge_add(int u, int v)
{
    static int cnt = 0;
    r[++cnt] = (Node){v, he[u]};
    he[u] = cnt;
}
LL f[100005][2], g[100005][2], h[100005][17][2][2];
int fa[100005][17], dep[100005];
void dfs1(int now)
{
    f[now][1] = a[now];
    PFOR(i, now)
    {
        int to = r[i].to;
        if(to == fa[now][0]) continue;
        fa[to][0] = now, dep[to] = dep[now] + 1;
        dfs1(to);
        f[now][0] += f[to][1], f[now][1] += std::min(f[to][0], f[to][1]);
    }
}
void dfs2(int now)
{
    PFOR(i, now)
    {
        int to = r[i].to;
        if(to == fa[now][0]) continue;
        g[to][0] = g[now][1] + (f[now][1] - std::min(f[to][0], f[to][1]));
        g[to][1] = std::min(g[to][0], g[now][0] + (f[now][0] - f[to][1]));
        dfs2(to);
    }
}
inline void Init()
{
    FOR(i, 2, n)
    {
        h[i][0][0][0] = INF;
        h[i][0][0][1] = h[i][0][1][1] = f[fa[i][0]][1] - std::min(f[i][0], f[i][1]);
        h[i][0][1][0] = f[fa[i][0]][0] - f[i][1];
    }
    FOR(j, 1, 16)
    {
        FOR(i, 2, n)
        {
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
            if(!fa[i][j]) continue;
            FOR(a, 0, 1) FOR(b, 0, 1)
            {
                h[i][j][a][b] = INF;
                FOR(c, 0, 1)
                    h[i][j][a][b] = std::min(h[i][j][a][b], h[i][j - 1][a][c] + h[fa[i][j - 1]][j - 1][c][b]);
            }
        }
    }
}
inline LL work(int u, int x, int v, int y)
{
    LL lu[2] = {INF, INF}, nu[2] = {INF, INF};
    LL lv[2] = {INF, INF}, nv[2] = {INF, INF};
    lu[x] = f[u][x];
    ROF(i, 16, 0)
        if(dep[fa[u][i]] >= dep[v])
        {
            FOR(j, 0, 1) FOR(k, 0, 1)
                nu[k] = std::min(nu[k], lu[j] + h[u][i][j][k]);
            lu[0] = nu[0], lu[1] = nu[1], nu[0] = nu[1] = INF,
            u = fa[u][i];
        }
    if(u == v) return lu[y] + g[v][y];
    lv[y] = f[v][y];
    ROF(i, 16, 0)
        if(fa[u][i] != fa[v][i])
        {
            FOR(j, 0, 1) FOR(k, 0, 1)
                nu[k] = std::min(nu[k], lu[j] + h[u][i][j][k]),
                nv[k] = std::min(nv[k], lv[j] + h[v][i][j][k]);
            lu[0] = nu[0], lu[1] = nu[1], nu[0] = nu[1] = INF, u = fa[u][i];
            lv[0] = nv[0], lv[1] = nv[1], nv[0] = nv[1] = INF, v = fa[v][i];
        }
    int lca = fa[u][0];
    LL re[2];
    re[0] = f[lca][0] + g[lca][0] - f[u][1] - f[v][1] + lu[1] + lv[1];
    re[1] = f[lca][1] + g[lca][1] - std::min(f[u][0], f[u][1]) - std::min(f[v][0], f[v][1]) + std::min(lu[0], lu[1]) + std::min(lv[0], lv[1]);
    return std::min(re[0], re[1]);
}
signed main()
{
#ifndef ONLINE_JUDGE
    freopen("test.in", "r", stdin);
    freopen("test.out", "w", stdout);
#endif
    n = reads(), m = reads(); scanf("%s", &_);
    FOR(i, 1, n) a[i] = reads();
    FOR(i, 1, n - 1)
    {
        int u = reads(), v = reads();
        Edge_add(u, v), Edge_add(v, u);
    }
    dep[1] = 1, dfs1(1), dfs2(1), Init();
    FOR(i, 1, m)
    {
        int u = reads(), x = reads(), v = reads(), y = reads();
        if(dep[u] < dep[v]) std::swap(u, v), std::swap(x, y);
        if(!x && !y && fa[u][0] == v) {puts("-1"); continue;}
        printf("%lld\n", work(u, x, v, y));
    }
    return 0;
}

标签:std,NOIP2018,fa,reads,lu,include,P5024,nu,保卫
来源: https://www.cnblogs.com/zuytong/p/16589447.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有