題目大意:給你一張n個點m條邊的圖。每次操作可以把兩個點合併成一個(與之相連的邊也都要連到新點上)。求把圖中每個聯通塊都變成“毛毛蟲”的最小操作次數。“毛毛蟲”必須是一棵樹(可以存在自環),且其中必須存在一條主鏈,使得主鏈外的點到主鏈上的任意一點距離爲1。$n\leq 2000,m\leq 10^{5}$
樹上亂搞題目×1
首先把原圖用邊雙$tarjan$縮成一棵樹
然後我們想辦法找出這條主鏈,似乎也只能$DP$了
定義$f(x,0/1)$表示 主鏈一端在$x$子樹內另一端在$x$點/主鏈兩個端點都在$x$子樹內 付出的最小代價
對於不在主鏈上的點,需要把整顆子樹合併成一個點,畫一畫圖發現這個數量就是這個子樹內的葉節點數量!
$f(x,0)=min{f(v,0)+合併其他子樹的花費}$
$f(x,1)=min(f(v_{0},0)+f(v_{1},0)+合併其他子樹的花費)$
合併其他子樹的花費很好推,但式子太長就不放了
這也意味着我們$DP$時需要挑一個非葉節點爲根進行$DP$
題目並沒有保證所有點都在一個聯通塊內..每個聯通塊都要統計一遍,好麻煩..
時間可以被菊花圖卡成$O(n^{2})$,但仍能輕鬆通過全部數據
1 #include <cstdio> 2 #include <cstring> 3 #include <algorithm> 4 #define N1 2010 5 #define M1 100050 6 using namespace std; 7 const int inf=0x3f3f3f3f; 8 9 template <typename _T> void read(_T &ret) 10 { 11 ret=0; _T fh=1; char c=getchar(); 12 while(c<'0'||c>'9'){ if(c=='-') fh=-1; c=getchar(); } 13 while(c>='0'&&c<='9'){ ret=ret*10+c-'0'; c=getchar(); } 14 ret=ret*fh; 15 } 16 17 struct Edge{ 18 int to[M1*2],nxt[M1*2],val[M1*2],head[N1],cte; 19 void ae(int u,int v,int w) 20 { cte++; to[cte]=v; nxt[cte]=head[u]; val[cte]=w; head[u]=cte; } 21 }e,g; 22 23 int n,m,num; 24 int dfn[N1],low[N1],use[N1],stk[N1],dad[N1],tp,tim,que[N1],tl; 25 int f[N1][2],sum[N1],sz[N1],inc[N1],vis[N1]; 26 void init() 27 { 28 memset(f,0,(num+1)*8); memset(sz,0,(num+1)*4); memset(sum,0,(num+1)*4); 29 memset(vis,0,(num+1)*4); memset(inc,0,(num+1)*4); memset(g.head,0,(num+1)*4); 30 tl=tp=tim=num=0; g.cte=0; 31 } 32 33 void tarjan(int x,int fa) 34 { 35 int j,v; que[++tl]=x; 36 dfn[x]=low[x]=++tim; use[x]=1; stk[++tp]=x; 37 for(j=e.head[x];j;j=e.nxt[j]) 38 { 39 if(e.val[j]==fa) continue; 40 v=e.to[j]; 41 if(!dfn[v]){ 42 tarjan(v,e.val[j]); 43 low[x]=min(low[x],low[v]); 44 }else if(use[v]){ 45 low[x]=min(low[x],dfn[v]); 46 } 47 } 48 if(low[x]==dfn[x]) 49 { 50 num++; 51 while(stk[tp]!=x) 52 use[stk[tp]]=0, dad[stk[tp]]=num, tp--; 53 use[stk[tp]]=0, dad[stk[tp]]=num, tp--; 54 } 55 } 56 57 void dfs(int x,int fa) 58 { 59 int j,v; sz[x]=1; vis[x]=1; 60 for(j=g.head[x];j;j=g.nxt[j]) 61 { 62 v=g.to[j]; if(v==fa) continue; 63 dfs(v,x); 64 sz[x]+=sz[v]; sum[x]+=sum[v]; 65 } 66 int k,v0,v1; 67 if(inc[x]>1) f[x][0]=f[x][1]=inf; else sum[x]=1; 68 for(j=g.head[x];j;j=g.nxt[j]) 69 { 70 v0=g.to[j]; if(v0==fa) continue; 71 f[x][0]=min(f[x][0],f[v0][0]+(sz[x]-1-sz[v0])-(sum[x]-sum[v0])); 72 for(k=g.nxt[j];k;k=g.nxt[k]) 73 { 74 v1=g.to[k]; if(v1==fa) continue; 75 f[x][1]=min(f[x][1],f[v0][0]+f[v1][0]+(sz[x]-sz[v0]-sz[v1]-1)-(sum[x]-sum[v0]-sum[v1])); 76 } 77 } 78 } 79 int de; 80 81 int solve(int x) 82 { 83 int i,j,ans=0,root; init(); 84 tarjan(x,0); 85 if(num<=2){ return tl-num; } 86 for(i=1;i<=tl;i++) 87 { 88 x=que[i]; 89 for(j=e.head[x];j;j=e.nxt[j]) if(dad[x]!=dad[e.to[j]]) 90 g.ae(dad[x],dad[e.to[j]],0), inc[dad[e.to[j]]]++; 91 } 92 for(i=1;i<=num;i++) if(inc[i]>1){ root=i; dfs(root,0); break; } 93 for(i=1,ans=inf;i<=num;i++) if(inc[i]>1) 94 if(i!=root) ans=min(ans,f[i][1]+(num-sz[i])-(sum[root]-sum[i])); 95 else ans=min(ans,f[i][1]); 96 return ans+tl-num; 97 } 98 99 int main() 100 { 101 int i,j,x,y,ans=-1; 102 scanf("%d%d",&n,&m); 103 for(i=1;i<=m;i++) read(x), read(y), e.ae(x,y,i), e.ae(y,x,i); 104 for(i=1;i<=n;i++) if(!dfn[i]) ans+=solve(i)+1; 105 printf("%d\n",ans); 106 return 0; 107 }