1. 程式人生 > >【BZOJ1487】無歸島(HNOI2009)-圓方樹+DP

【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; }