SPOJ 1825 Free tour II 樹分治


題意:

有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數組,不用區分,直接合並取最大值。

 

 

View Code
  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 }

感覺樹分治的細節挺多的。。。。囧。。。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM