基环树

什么是基环树###

简而言之就是一个环上连了几棵树,如图

模板城市环路

结合这道题,既然是基环树,那么首先肯定要先把环找出来,直接搜索就行了

void dfs1(int u, int from)
{
    st[u] = ins[u] = true;
    for (int i = head[u]; ~i; i = net[i])
    {
        if (i == (from ^ 1))
            continue;
        int v = ver[i];
        fu[v] = u;
        if (!st[v])
            dfs1(v, i);
        else if (ins[v])
        {
            for (int j = u; j != v; j = fu[j])//记录环上的结点
                cir[++tot] = j;
            cir[++tot] = v;
        }
    }
    ins[u] = false;
}

找出来这个环其余的就好办了,可以将这个环断开,对于环上的每个结点,做一遍树型DP即可,DP过程就不讲了,比较常见的类型

完整代码

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N = 1e5 + 5;
typedef long long ll;

ll g[N][2][2], f[N][2];
int p[N], cir[N], fu[N], fw[N], tot;
int head[2 * N], ver[2 * N], net[2 * N], idx;
bool st[N], ins[N];

void add(int a, int b)
{
    net[idx] = head[a], ver[idx] = b, head[a] = idx++;
}

void dfs1(int u, int from)
{
    st[u] = ins[u] = true;
    for (int i = head[u]; ~i; i = net[i])
    {
        if (i == (from ^ 1))
            continue;
        int v = ver[i];
        fu[v] = u;
        if (!st[v])
            dfs1(v, i);
        else if (ins[v])
        {
            for (int j = u; j != v; j = fu[j])
                cir[++tot] = j;
            cir[++tot] = v;
        }
    }
    ins[u] = false;
}

void dfs2(int u)
{
    f[u][1] = p[u], st[u] = true;
    for (int i = head[u]; ~i; i = net[i])
    {
        int v = ver[i];
        if (st[v])
            continue;
        dfs2(v);
        f[u][0] += max(f[v][0], f[v][1]);
        f[u][1] += f[v][0];
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    memset(head, -1, sizeof(head));
    for (int i = 1; i <= n; i++)
        scanf("%d", &p[i]);
    for (int i = 1; i <= n; i++)
    {
        int a, b;
        scanf("%d%d", &a, &b);
        add(a + 1, b + 1), add(b + 1, a + 1);
    }
    dfs1(1, -1);
    memset(st, 0, sizeof(st));
    for (int i = 1; i <= tot; i++)
        st[cir[i]] = true;
    for (int i = 1; i <= tot; i++)
        dfs2(cir[i]);
    g[1][0][0] = f[cir[1]][0];
    g[1][1][1] = f[cir[1]][1];
    for (int i = 2; i <= tot; i++)
    {
        int u = cir[i];
        g[i][0][0] = max(g[i - 1][0][0], g[i - 1][0][1]) + f[u][0];
        g[i][1][0] = max(g[i - 1][1][0], g[i - 1][1][1]) + f[u][0];
        g[i][0][1] = g[i - 1][0][0] + f[u][1], g[i][1][1] = g[i - 1][1][0] + f[u][1];
    }
    double k;
    scanf("%lf", &k);
    k *= max(g[tot][0][1], max(g[tot][1][0], g[tot][0][0]));
    printf("%.1lf", k);
    return 0;
}

练习题

P2607 [ZJOI2008]骑士

和上一道题类似,只不过是基环树森林

P4381 [IOI2008]Island

需要断环成链,然后单调队列优化DP,可以做到 \(O(n)\)

posted @ 2021-04-14 16:52  DSHUAIB  阅读(53)  评论(0编辑  收藏  举报