[洛谷P4410][HNOI2009]無歸島
ofollow,noindex" target="_blank">題目傳送門
本題有一個結論就是圖是邊仙人掌。接下來來證明這個結論:
【推論1】對於一個n個節點(n>1)的島嶼,至少有一個節點的度大於1。
證:
假設不存在度大於1的節點。
那麼如果n=2,圖的形態只有一種,不可行。
如果n>2,要使圖聯通,那麼邊數量一定至少為n-1,如果節點度至多為1,那麼邊數一定少與n-1條,所以也不可行。
綜上所述,至少有一個節點的度大於1。
證畢。
【推論2】對於一個n個節點的島嶼,該島嶼是邊仙人掌。
證:
對問題進行歸納證明。
當n=0或n=1時顯然成立。
當n>1時,
那麼根據【推論1】至少有一個節點的度大於1。設度數最大的節點為A。
不妨設其中一個和A有連邊的節點為B。那麼設與A,B均有連邊的點為C。
【推論2.1】B,C均沒有除AB,AC,BC以外的其它連邊。
證:
由於對稱性,我們只討論B的情況。
使用反證法,假設B的其他連邊連向D。
設任意一個與A有直接連邊的點E。那麼存在一個點與D,E均有連邊,設這個點為F。那麼對於另一個與A有連邊的點E’,與E’,D有連邊的點F’,由題意得,F’與F不同。那麼意味著D與所有與A有連邊的點均需要至少對應得與增加一條連邊,而D本身與B有連邊,所以此時D的度數大於A,與假設不相符,因此B無其他連邊。
對稱地,C也成立。
證畢。
根據【推論2.1】,B,C均沒有其他連邊,那麼根據歸納法,除去B,C以外的點是構成了邊仙人掌,所以整個島嶼是邊仙人掌。
【推論3】該圖是邊仙人掌。
證:
所有島嶼構成了一個環,島嶼與島嶼之間沒有直接連邊,所以一條邊處在兩個環中。
證畢。
所以這就是一道邊仙人掌DP的裸題,直接寫棵圓方樹DP一下就可以了。
\#include<bits/stdc++.h> using namespace std; const int MAX_N=5+2e5; struct Edge{ int to,nxt; }edge[2][MAX_N+MAX_N]; int head[2][MAX_N],top_edge[2]={-1,-1}; void add_edge(int t,int x,int y){ edge[t][++top_edge[t]]=(Edge){y,head[t][x]}; head[t][x]=top_edge[t]; } int a[MAX_N],stk[MAX_N],top_node,n; int dfn[MAX_N],low[MAX_N],ti=0,top_stk=0; void tarjan(int x,int pre){ stk[++top_stk]=x,dfn[x]=low[x]=++ti; for(int j=head[0][x];j!=-1;j=edge[0][j].nxt){ int y=edge[0][j].to; if(y!=pre){ if(dfn[y]==0){ tarjan(y,x),low[x]=min(low[x],low[y]); if(low[y]==dfn[x]){ ++top_node; add_edge(1,x,top_node); add_edge(1,top_node,x); while(stk[top_stk+1]!=y){ add_edge(1,stk[top_stk],top_node); add_edge(1,top_node,stk[top_stk]); --top_stk; } }else if(low[y]>dfn[x]){ add_edge(1,x,y); add_edge(1,y,x); } }else low[x]=min(low[x],dfn[y]); } } if(dfn[x]==low[x]) --top_stk; } int f[MAX_N][2],g[MAX_N][2][2],son[MAX_N]; bool mark[MAX_N]; void dp(int x,int pre){ if(mark[x]) return; mark[x]=true; int top=0; for(int j=head[1][x];j!=-1;j=edge[1][j].nxt){ int y=edge[1][j].to; if(y!=pre) dp(y,x); } for(int j=head[1][x];j!=-1;j=edge[1][j].nxt){ int y=edge[1][j].to; if(y!=pre) son[++top]=y; } if(x>n){ g[1][0][0]=f[son[1]][0],g[1][0][1]=0; g[1][1][0]=0,g[1][1][1]=f[son[1]][1]; for(int j=2;j<=top;++j){ int y=son[j]; g[j][0][0]=max(g[j-1][0][0],g[j-1][0][1])+f[y][0]; g[j][0][1]=g[j-1][0][0]+f[y][1]; g[j][1][0]=max(g[j-1][1][0],g[j-1][1][1])+f[y][0]; g[j][1][1]=g[j-1][1][0]+f[y][1]; } f[x][0]=g[top][0][0]; f[x][1]=max(max(g[top][0][1],g[top][1][0]),g[top][1][1]); }else{ f[x][0]=0,f[x][1]=a[x]; for(int j=1;j<=top;++j){ int y=son[j]; f[x][0]+=max(f[y][0],f[y][1]); f[x][1]+=f[y][0]; } } } int main(){ memset(head,-1,sizeof(head)); int m; scanf("%d%d",&n,&m); top_node=n; for(int i=1;i<=m;++i){ int u,v; scanf("%d%d",&u,&v); add_edge(0,u,v),add_edge(0,v,u); } for(int i=1;i<=n;++i){ scanf("%d",&a[i]); a[i]=max(a[i],0); } int ans=0; for(int i=1;i<=n;++i) if(dfn[i]==0) tarjan(i,0); for(int i=1;i<=n;++i) if(!mark[i]){ dp(i,0); ans+=max(f[i][0],f[i][1]); } printf("%d\n",ans); return 0; }