嘘~ 正在从服务器偷取页面 . . .

洛谷P2478 [SDOI2010]城市规划 题解


洛谷P2478 [SDOI2010]城市规划 题解

题目链接:P2478 [SDOI2010]城市规划

题意

给定一个带点权沙漠(多棵仙人掌),求最大权独立集。

此处独立集的定义为集合中任意两个结点的最短路径长度不小于 $3$ (就是隔两个结点)

独立集的权值定义为独立集中所有的点的权值的和。

数据范围

$n\le 10^6,m\le 2\times 10^6$,且点权 $0\le h_i \le 1000$。

提示

原题给出的是点仙人掌组成的沙漠。

点仙人掌的定义为:每个点至多存在于一个简单环内的无向连通图。

边仙人掌的定义为:每条边至多存在于一个简单环内的无向连通图。

考虑这样一张图 (1,2) (1,3) (2,3) (3,4) (3,5) (4,5) 。它就是边仙人掌,而不是点仙人掌。

仙人掌dp套dp神题,想了我大概 $5$ 个小时(其实是沙漠

我们可以用 $\mathtt{Tarjan}$ 找环,然后分别考虑树上dp和环上dp。

设 $f_{i,0/1/2}$ ,则

  • $f_{i,0}$ 表示选了 $i$ , $i$ 所在子树的最大价值
  • $f_{i,1}$ 表示选了 $i$ 的某个儿子, $i$ 所在子树的最大价值
  • $f_{i,2}$ 表示 $i$ 和它儿子都没选,子树的最大价值

这里子树的定义为

  • 若 $u$ 不在环上或 $u$ 是环顶,则子树定义为缩点后 $\mathtt{DFS}$ 树上的子树,不考虑返祖边和横叉边。
  • 若 $u$ 在环上但不是环顶,则子树定义为 $\mathtt{DFS}$ 树上「所有不在环上的儿子」的子树与 $u$ 的并。

下文中使用 $a \uparrow b+c$ 表示 a += b+c 。在讲解之前先给一张图方便理解。

树上dp部分

显然叶子结点有 $f_{u,0}=h_u,~f_{u,1}=f_{u,2}=0$ 。

考虑转移。设 $u$ 为非叶子结点, $v \in \mathtt{son}(u)$ ,则

显然 $f_{u,1}$ 需要在 $f_{u,0},f_{u,2}$ 计算完再计算

因为恰好只能选一个儿子,所以我们可以按 $f_{u,2}$ 的方式转移,并考虑选的那个儿子对应的增量,则

注意到后面那个式子是可以在算 $f_{u,0}$ 和 $f_{u,2}$ 的时候算出来的。

因为用了 $\mathtt{Tarjan}$ ,所以在做 $u$ 的树上dp部分时,不会把 $u$ 的环上dp部分给多算进去。


环上dp部分

设环顶为 $u$ (即 $\mathtt{DFS}$ 树上深度最小的那个点)

记环上点按深度大到小依次为 $c_1,c_2,\cdots,c_k$ ,则有 $c_k = u$ 。

设 $\mathtt{calc}(l,r)$ 表示规定了环上 $c_{l-1},c_{r+1}$ 不能选,他们之间的结点 $c_{l\sim r}$按照题意随便选的最大价值。

类似于 $f$ 的定义,对于每个要处理的环,我们设一个 $g_{i,0/1/2}$ 。

  • $g_{i,0}$ 表示选了 $c_i$ ,$c_i$ 所在子树的最大价值。
  • $g_{i,1}$ 表示选了 $c_i$ 的某个儿子,$c_i$ 所在子树的最大价值。
  • $g_{i,2}$ 表示 $c_i$ 和儿子都没选,$c_i$ 所在子树的最大价值

边界:$g_{l-1,0} = 0, ~g_{l-1,1} = f_{c_{l-1},1},~g_{l-1,2} = f_{c_{l-1},2}$ 。

考虑转移,不难发现有

注意 $l \le i \le r+1$ ,$\mathtt{calc}(l,r) = \max\{g_{r+1,1},~g_{r+1,2}\}$ 。

特别地,如果 $l > r+1$ ,则定义 $\mathtt{calc}(l,r)=0$ 。

接着考虑 $f_{u,0/1/2}$ (环顶)的转移,不难发现(注意这里还是增量,因为可能不止一个儿子在环上)

$f_{u,1}$ 不同于树上dp部分,要在 $f_{u,0},f_{u,2}$ 之前算。

  • 选择了环外子结点,则 $f_{u,1} \gets f_{u,1} + \mathtt{calc}(2,k-2)$
  • 选择了 $c_1$ ,则 $f_{u,1} \gets f_{u,2} + f_{c_2,2} + f_{c_1,0} + \mathtt{calc}(4,k-2)$
  • 选择了 $c_{k-1}$ ,则 $f_{u,1} \gets f_{u,2} + f_{c_{k-2},2} + f_{c_{k-1},0} + \mathtt{calc}(2,k-4)$

综上,有

时间复杂度 $\mathcal{O}(n+m)$

代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define INF 0x3f3f3f3f3f3f3f3f
void up(int &x,int y) { x < y ? x = y : 0; }
void down(int &x,int y) { x > y ? x = y : 0; }
namespace FastIO
{
    #define gc() readchar()
    #define pc(a) putchar(a)
    #define SIZ (int)(1e6+15)
    char buf1[SIZ],*p1,*p2;
    char readchar()
    {
        if(p1==p2)p1=buf1,p2=buf1+fread(buf1,1,SIZ,stdin);
        return p1==p2?EOF:*p1++;
    }
    template<typename T>void read(T &k)
    {
        char ch=gc();T x=0,f=1;
        while(!isdigit(ch)){if(ch=='-')f=-1;ch=gc();}
        while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=gc();}
        k=x*f;
    }
    template<typename T>void write(T k)
    {
        if(k<0){k=-k;pc('-');}
        static T stk[66];T top=0;
        do{stk[top++]=k%10,k/=10;}while(k);
        while(top){pc(stk[--top]+'0');}
    }
    template<typename T>void print(T x) { write(x); pc('\n'); }
}using namespace FastIO;
#define N ((int)(1e6+15))

int n,m,tim,pos=1,head[N],tmp[N][3],h[N],c[N],f[N][3],fa[N],dfn[N],low[N];
struct Edge{ int u,v,next; } e[N * 2];
void addEdge(int u,int v) { e[++pos] = {u,v,head[u]}; head[u] = pos; }
int calc(int l,int r)
{
    if(l > r+1) return 0;
    tmp[l-1][0] = 0;
    tmp[l-1][1] = f[c[l-1]][1];
    tmp[l-1][2] = f[c[l-1]][2];
    for(int i=l; i<=r+1; i++)
    {
        tmp[i][0] = tmp[i-1][2] + f[c[i]][0];
        tmp[i][2] = max(tmp[i-1][1], tmp[i-1][2]) + f[c[i]][2];
        tmp[i][1] = max(tmp[i-1][0] + f[c[i]][2], max(tmp[i-1][1],tmp[i-1][2]) + f[c[i]][1]);
    }
    // cout << l << "  " << r << "  " << max(tmp[r+1][1],tmp[r+1][2]) << '\n';
    return max(tmp[r+1][1], tmp[r+1][2]);
}
void solve(int s,int t)
{
    int cnt = 0;
    for(c[++cnt] = s; c[cnt] != t; ++cnt)
        c[cnt + 1] = fa[c[cnt]];

    int mx = max(f[c[1]][0] + f[c[2]][2] + calc(4,cnt-2),
         f[c[cnt-1]][0] + f[c[cnt-2]][2] + calc(2,cnt-4));
    f[t][1] = max(f[t][1] + calc(2,cnt-2), f[t][2] + mx);
    f[t][0] += calc(3, cnt-3) + f[c[1]][2] + f[c[cnt-1]][2];
    f[t][2] += calc(2, cnt-2);
}
void Tarjan(int u,int Fa)
{
    fa[u] = Fa; dfn[u] = low[u] = ++tim;
    f[u][0] = h[u]; f[u][2] = 0;
    int mx = 0;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v = e[i].v;
        if(!dfn[v])
        {
            Tarjan(v,u); down(low[u], low[v]);
        }else if(v != Fa)
        { down(low[u], dfn[v]); }
        if(low[v] > dfn[u])
        {
            f[u][0] += f[v][2];
            f[u][2] += max(f[v][2], f[v][1]);
            up(mx, f[v][0] - max(f[v][2], f[v][1]));
        }
    }
    f[u][1] = f[u][2] + mx;
    for(int i=head[u]; i; i=e[i].next)
    {
        int v = e[i].v;
        if(low[v] == dfn[u] && fa[v] != u) solve(v,u);
    }
}
signed main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);cout.tie(0);
    // freopen("check.in","r",stdin);
    // freopen("check.out","w",stdout);
    read(n); read(m);
    for(int i=1; i<=n; i++) read(h[i]);
    for(int i=1,u,v; i<=m; i++)
    {
        read(u); read(v);
        addEdge(u,v); addEdge(v,u);
    }
    int ans = 0;
    for(int i=1; i<=n; i++) if(!dfn[i])
    { Tarjan(i,0); ans += max({f[i][0], f[i][1], f[i][2]}); }
    print(ans);
    return 0;
}

参考文献

[1] https://www.luogu.com.cn/blog/ix-35/solution-p2478


文章作者: q779
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 许可协议。转载请注明来源 q779 !
评论
  目录