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