【BZOJ1487】无归岛(HNOI2009)-圆方树+DP

测试地址:无归岛
做法:本题需要用到圆方树+DP。
很显然题目中所给的图是一个仙人掌,那么这道题要求的就是仙人掌上的最大点权和独立集。
于是我们把仙人掌上的问题转化成圆方树上的问题。圆点上的DP很好处理,像树形DP一样处理即可,主要是方点上的DP,由于方点所在的环和它上面的圆点有两个相邻的点,所以要进行特殊判断,也就是对于一般的情况而言,dp(i,0/1)表示点i选或不选的最大点权和,而对于一个环,我们要令dp(i,0/1)表示它与上面的圆点相邻的点选或不选的最大点权和。为了方便,我们令dp(i,0)为不选某点的最大点权和,dp(i,1)可选可不选某点的最大点权和,这就很好做DP了,时间复杂度为O(n),可以通过此题。
以下是本人代码:

#include <bits/stdc++.h>
using namespace std;
const int N=100010;
const int M=N<<2;
typedef long long ll;
int n,m,first[N]={0},firsted[N<<1]={0},tot=0,totpbc;
int low[N],dfn[N],tim=0,st[N],top=0;
int fa[N],fae[N],cir[N];
ll val[N],dp[N<<1][2]={0},cirdp[N][2];
bool vis[N],inst[N];
struct edge
{
    int v,next,id;
}e[M],ed[M];

void insert(int a,int b,int id)
{
    e[++tot].v=b;
    e[tot].next=first[a];
    e[tot].id=id;
    first[a]=tot;
}

void inserted(int a,int b)
{
    ed[++tot].v=b;
    ed[tot].next=firsted[a];
    firsted[a]=tot;
}

void combine(int x,int y)
{
    int now=y;
    cir[0]=0;
    while(now!=x)
    {
        cir[++cir[0]]=now;
        now=fa[now];
    }
    cir[++cir[0]]=x;

    cirdp[0][0]=cirdp[0][1]=0;
    for(int i=1;i<cir[0];i++)
    {
        cirdp[i][0]=cirdp[i-1][1]+dp[cir[i]][0];
        cirdp[i][1]=cirdp[i-1][0]+dp[cir[i]][1];
        cirdp[i][1]=max(cirdp[i][0],cirdp[i][1]);
    }
    dp[++totpbc][1]=cirdp[cir[0]-1][1];

    cirdp[1][0]=cirdp[1][1]=0;
    for(int i=2;i<cir[0]-1;i++)
    {
        cirdp[i][0]=cirdp[i-1][1]+dp[cir[i]][0];
        cirdp[i][1]=cirdp[i-1][0]+dp[cir[i]][1];
        cirdp[i][1]=max(cirdp[i][0],cirdp[i][1]);
    }
    if (cir[0]==2) dp[totpbc][0]=dp[1][0];
    else dp[totpbc][0]=cirdp[cir[0]-2][1]+dp[cir[1]][0]+dp[cir[cir[0]-1]][0];
    dp[totpbc][1]=max(dp[totpbc][1],dp[totpbc][0]);

    inserted(x,totpbc);
    for(int i=1;i<cir[0];i++)
        inserted(totpbc,cir[i]);
}

void tarjan(int v,int laste)
{
    vis[v]=inst[v]=1;
    low[v]=dfn[v]=++tim;
    st[++top]=v;
    int now=top;
    for(int i=first[v];i;i=e[i].next)
        if (e[i].id!=laste)
        {
            if (!vis[e[i].v])
            {
                fa[e[i].v]=v;
                fae[e[i].v]=e[i].id;
                tarjan(e[i].v,e[i].id);
                if (low[e[i].v]>dfn[v])
                {
                    top--;
                    inst[e[i].v]=0;
                    inserted(v,e[i].v);
                }
                if (low[e[i].v]==dfn[v])
                {
                    for(int i=top;i>now;i--)
                        inst[st[i]]=0;
                    top=now;
                }
                low[v]=min(low[v],low[e[i].v]);
            }
            else if (inst[e[i].v]) low[v]=min(low[v],dfn[e[i].v]);
        }
    for(int i=first[v];i;i=e[i].next)
        if (fae[e[i].v]!=e[i].id&&dfn[v]<dfn[e[i].v]) combine(v,e[i].v);
    for(int i=firsted[v];i;i=ed[i].next)
    {
        dp[v][0]+=dp[ed[i].v][1];
        dp[v][1]+=dp[ed[i].v][0];
    }
    dp[v][1]+=val[v];
    dp[v][1]=max(dp[v][1],dp[v][0]);
}

int main()
{
    scanf("%d%d",&n,&m);
    for(int i=1;i<=m;i++)
    {
        int a,b;
        scanf("%d%d",&a,&b);
        insert(a,b,i),insert(b,a,i);
    }
    for(int i=1;i<=n;i++)
        scanf("%lld",&val[i]);

    totpbc=n;
    tot=0;
    fa[1]=0;
    tarjan(1,0);
    printf("%lld",dp[1][1]);

    return 0;
}
posted @ 2018-05-06 19:54  Maxwei_wzj  阅读(115)  评论(0编辑  收藏  举报