【BZOJ1487】無歸島(HNOI2009)-圓方樹+DP
阿新 • • 發佈:2019-02-05
測試地址:無歸島
做法:本題需要用到圓方樹+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;
}