bzoj 2286

第一道"虚树"题目(好吧,我也不知道这是不是虚树,但和虚树的思想肯定是一样的,都是简化树结构)

 

这一类算法核心思想都是简化树结构,只取我们必须的节点和一些信息,然后在简化后的树结构上工作。

 

首先,如果这道题只有一次询问,那么很容易想到树形DP的解法,但这道题又多组询问,并且限制了所有询问的关键点个数,这意味着我们必须设计出一种算法,她回答一组询问的复杂度只和关键点个数相关(O(k)或O(klogk)都是可接受的),而和原图无关(最多加个logn)。

 

然后就有了虚树,我们可以构建一个新的树,这棵树上有所有关键点,以及相邻dfs序上相邻的两个关键点的lca,我们发现,这样的图包括了所有关键点的lca以及所有关键点,然后改一下DP就可以在这棵树上快速的搞了(因为节点个数是O(2*k),所以这样DP的复杂度就从O(n)变成了O(k))。

DP:

dp[i]表示将i及其子树中所有关键点与跟节点断开所需的最小代价(可以砍他们上面的边)

 

构简化图:

对于一个询问,我们先将其关键点按照DFS序排序。然后维护一个栈保存当前走了的关键点或关键点之间的LCA,当我们要插入一个新的关键节点时,我们根据当前节点与当前栈顶节点LCA的深度与栈顶元素的深度来判断是否需要弹出节点,一直弹出,直到深度大于等于栈顶元素(注意,这个LCA是最初的栈顶与新的关键节点的LCA)

 

  1 /**************************************************************
  2     Problem: 2286
  3     User: idy002
  4     Language: C++
  5     Result: Accepted
  6     Time:6700 ms
  7     Memory:32060 kb
  8 ****************************************************************/
  9  
 10 #include <cstdio>
 11 #include <vector>
 12 #include <algorithm>
 13 #define min(a,b) ((a)<(b)?(a):(b))
 14 #define oo 0x3f3f3f3f
 15 #define N 250010
 16 #define P 17
 17 using namespace std;
 18  
 19 typedef long long dnt;
 20  
 21 int n, m;
 22 int head[N], dest[N+N], wght[N+N], next[N+N], ntot;
 23 int dfn[N], dep[N], bst[N], anc[N][P+1], idc;
 24 int qcnt, aa[N], stk[N], top;
 25 dnt dp[N];
 26  
 27 void adde( int u, int v, int w ) {
 28     ntot++;
 29     wght[ntot] = w;
 30     dest[ntot] = v;
 31     next[ntot] = head[u];
 32     head[u] = ntot;
 33 }
 34 void dfs( int u ) {
 35     dfn[u] = ++idc;
 36     for( int p=1; p<=P; p++ )
 37         anc[u][p] = anc[anc[u][p-1]][p-1];
 38     for( int t=head[u]; t; t=next[t] ) {
 39         int v=dest[t], w=wght[t];
 40         if( v==anc[u][0] ) continue;
 41         anc[v][0] = u;
 42         bst[v] = min( bst[u], w );
 43         dep[v] = dep[u]+1;
 44         dfs(v);
 45     }
 46 }
 47 bool cmp( int u, int v ) {
 48     return dfn[u]<dfn[v];
 49 }
 50 int lca( int u, int v ) {
 51     if( dep[u]<dep[v] ) swap(u,v);
 52     int t=dep[u]-dep[v];
 53     for( int p=0; t; t>>=1,p++ )
 54         if( t&1 ) u=anc[u][p];
 55     if( u==v ) return u;
 56     for( int p=P; p>=0 && anc[u][0]!=anc[v][0]; p-- )
 57         if( anc[u][p]!=anc[v][p] ) 
 58             u=anc[u][p], v=anc[v][p];
 59     return anc[u][0];
 60 }
 61 void sov() {
 62     scanf( "%d", &qcnt );
 63     for( int i=1; i<=qcnt; i++ ) 
 64         scanf( "%d", aa+i );
 65     sort( aa+1, aa+1+qcnt, cmp );
 66      
 67     stk[top=1] = 1;
 68     dp[1] = 0;
 69     for( int i=1; i<=qcnt; i++ ) {
 70         int ca=lca(aa[i],stk[top]);
 71         while( dep[stk[top]]>dep[ca] ) {
 72             int fa, u;
 73             u = stk[top];
 74             top--;
 75             if( dep[stk[top]]<=dep[ca] ) {
 76                 if( dep[stk[top]]<dep[ca] ) {
 77                     stk[++top] = ca;
 78                     dp[ca] = 0;
 79                 }
 80                 fa = stk[top];
 81  
 82                 dp[u] = min( dp[u], bst[u] );
 83                 dp[fa] += dp[u];
 84                 break;
 85             } 
 86             fa = stk[top];
 87  
 88             dp[u] = min( dp[u], bst[u] );
 89             dp[fa] += dp[u];
 90         }
 91         int u=aa[i];
 92         stk[++top] = u;
 93  
 94         dp[u] = bst[u];
 95     }
 96     while( top ) {
 97         if( top-1 ) {
 98             int fa=stk[top-1], u=stk[top];
 99  
100             dp[u] = min( dp[u], bst[u] );
101             dp[fa] += dp[u];
102         }
103         top--;
104     }
105     printf( "%lld\n", dp[1] );
106 }
107 int main() {
108     scanf( "%d", &n );
109     for( int i=1,u,v,w; i<n; i++ ) {
110         scanf( "%d%d%d", &u, &v, &w );
111         adde( u, v, w );
112         adde( v, u, w );
113     }
114     anc[1][0] = 1;
115     dep[1] = 1;
116     bst[1] = oo;
117     dfs(1);
118     scanf( "%d", &m );
119     for( int i=1; i<=m; i++ )
120         sov();
121 }
View Code

 

posted @ 2015-04-05 12:15  idy002  阅读(239)  评论(0编辑  收藏  举报