題意:
有N個頂點的樹,節點間有權值, 節點分為黑點和白點。 找一條最長路徑使得 路徑上黑點數量不超過K個。
題解:
此題是qzc的論文里的題,沒看懂qzc寫的,后來看的別人的代碼才理解了。
先引用一下這位神犇的題解:http://hi.baidu.com/fuqbppvrgcbactd/item/14a81a1bdbd9f98888a956b9
在以ROOT為根的樹上,我們可以這樣表示狀態:F[ i , j ] 表示它的第 I 個子樹中經過的不超過 J 個黑點的路徑中,最長的一條的長度是多少,這樣可以保證 F[ I , J ] 的遞增性。要求出F[ I , J ] ,我們只要對所有子樹進行一次DFS即可,復雜度是O( N )的。不過如果要保存這樣的狀態對於某些數據可能有些困難,因為數據范圍太大了,我們可以通過以下方法來優化:我們要求的 F[ i , j ] 是把所有做過的子樹全部保存起來,不過我們要用的只是對於每個 J 的最大值!所以我們可以根據這一點進行一個對空間的優化。把 F[ i , j ] 變成一維的 F[ i ] 表示當前已經計算過的子樹中,經過黑點數不超過 i 個的路徑中最長的長度是多少,對於當前所計算的子樹,用 G[ i ] 表示當前子樹中經過黑色點數嚴格為 i 個的路徑中最長的路徑的長度是多少。可以比較G[ i ] 和 F[ i ] 的大小來更新 F[ i ] ,依然可以保證 F[ i ] 的遞增。
不過每次更新一次 F[ i ] 的時候的復雜度是 F[ i ] 和 G[ i ] 深度的最大值,對於某些數據可能達到O( n^2 ) ,所以在進行更新之前對子樹進行一次排序,關鍵字是該子樹中路徑經過最多數量的黑色節點的數量。
我就是沒有看懂怎么從n^2變成logn的,所以特別解釋一下:
因為最多只有n個黑色的節點,所以枚舉黑色節點的個數進行更新即可,即對於下標相同的F數組,不用區分,直接合並取最大值。

1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <cstdlib> 5 #include <algorithm> 6 7 #define N 450010 8 #define M 1100000 9 #define INF 1e8 10 11 using namespace std; 12 13 int head[N],next[M],len[M],to[M]; 14 bool vis[N]; 15 int bk[N],sz[N],lim[N],num[N],dep[N],g[N],mg[N]; 16 int n,m,md,root,cnt,mn,ans; 17 18 inline void init() 19 { 20 memset(head,-1,sizeof head); cnt=0; 21 memset(bk,0,sizeof bk); 22 } 23 24 inline bool cmp(int x,int y) 25 { 26 return dep[to[x]]<dep[to[y]]; 27 } 28 29 inline void add(int u,int v,int w) 30 { 31 to[cnt]=v; len[cnt]=w; next[cnt]=head[u]; head[u]=cnt++; 32 } 33 34 inline void read() 35 { 36 init(); 37 scanf("%d%d%d",&n,&md,&m); 38 for(int i=1,a;i<=m;i++) 39 scanf("%d",&a),bk[a]=1; 40 for(int i=1,a,b,c;i<n;i++) 41 { 42 scanf("%d%d%d",&a,&b,&c); 43 add(a,b,c); add(b,a,c); 44 } 45 } 46 47 inline void getsize(int u,int fa) 48 { 49 sz[u]=1; lim[u]=0; 50 for(int i=head[u];~i;i=next[i]) 51 if(to[i]!=fa&&!vis[to[i]]) 52 { 53 getsize(to[i],u); 54 sz[u]+=sz[to[i]]; 55 lim[u]=max(lim[u],sz[to[i]]); 56 } 57 } 58 59 inline void getroot(int p,int u,int fa) 60 { 61 lim[u]=max(lim[u],sz[p]-sz[u]); 62 if(lim[u]<mn) mn=lim[u],root=u; 63 for(int i=head[u];~i;i=next[i]) 64 if(to[i]!=fa&&!vis[to[i]]) getroot(p,to[i],u); 65 } 66 67 inline void getdep(int u,int fa) 68 { 69 dep[u]=bk[u]; int res=0; 70 for(int i=head[u];~i;i=next[i]) 71 if(to[i]!=fa&&!vis[to[i]]) 72 { 73 getdep(to[i],u); 74 res=max(res,dep[to[i]]); 75 } 76 dep[u]+=res; 77 } 78 79 inline void getg(int u,int fa,int d,int c) 80 { 81 g[c]=max(g[c],d); 82 for(int i=head[u];~i;i=next[i]) 83 if(to[i]!=fa&&!vis[to[i]]) 84 getg(to[i],u,d+len[i],c+bk[to[i]]); 85 } 86 87 inline void getans(int u,int fa) 88 { 89 getsize(u,fa); 90 mn=INF; 91 getroot(u,u,fa); 92 int rt=root,tot=0; 93 vis[rt]=true; 94 for(int i=head[rt];~i;i=next[i]) 95 if(!vis[to[i]]) getans(to[i],to[i]); 96 for(int i=head[rt];~i;i=next[i]) 97 if(!vis[to[i]]) 98 { 99 getdep(to[i],rt); 100 num[++tot]=i; 101 } 102 sort(num+1,num+1+tot,cmp); 103 for(int i=0;i<=dep[to[num[tot]]];i++) mg[i]=-INF; 104 for(int i=1;i<=tot;i++) 105 { 106 int v=to[num[i]],d=dep[v]; 107 int val=len[num[i]]; 108 for(int j=0;j<=d;j++) g[j]=-INF; 109 getg(v,rt,val,bk[v]); 110 if(i!=1) 111 { 112 for(int j=0;j<=md-bk[rt]&&j<=d;j++) 113 { 114 int sa=min(dep[to[num[i-1]]],md-bk[rt]-j); 115 if(mg[sa]==-INF) break; 116 if(g[j]!=-INF) ans=max(ans,mg[sa]+g[j]); 117 } 118 } 119 for(int j=0;j<=d;j++) 120 { 121 mg[j]=max(g[j],mg[j]); 122 if(j) mg[j]=max(mg[j],mg[j-1]); 123 if(j+bk[rt]<=md) ans=max(ans,mg[j]); 124 } 125 } 126 vis[rt]=false; 127 } 128 129 inline void go() 130 { 131 memset(vis,0,sizeof vis); 132 getans(1,1); 133 printf("%d\n",ans); 134 } 135 136 int main() 137 { 138 read(); 139 go(); 140 return 0; 141 }
感覺樹分治的細節挺多的。。。。囧。。。