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 }

时间: 2024-10-10 16:15:08

bzoj 2286的相关文章

bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 1276  Solved: 445[Submit][Status][Discuss] Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸

bzoj 2286: [Sdoi2011消耗战

1 #include<cstdio> 2 #include<iostream> 3 #define M 1000009 4 #define N 250009 5 #define ll long long 6 #define inf 1000000000000000000LL 7 #include<algorithm> 8 using namespace std; 9 int n,head[N],next[M],u[M],cnt,fa[N][22],deep[N],m,h

消耗战(bzoj 2286)

Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿.由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小. 侦查部门还发现,敌军有一台神秘机器.即使我军切断所有能源之后,他们也可

BZOJ 2286 消耗战

虚树裸题. 23333以后memset千万慎用. #include<iostream> #include<cstdio> #include<cstring> #include<algorithm> #define maxv 250050 #define maxe 500050 #define inf 0x7f7f7f7f7f7f7f7fLL using namespace std; struct edge { long long v,w,nxt; }e[ma

BZOJ 2286: [Sdoi2011消耗战 [DP 虚树]

传送门 题意: 删除价值和最小的边使得$1$号点与$k$个关键点不连通 一个树形DP...但是询问多次,保证总的关键点数为$O(n)$ 先说一下这个$DP$ $f[i]$表示子树$i$中的关键点与$1$不连通的最小价值 如果$i$是关键点则必须删除$i$到$1$的权值最小的边,否则$\sum f[child\ of\ i]$ 学了一下虚树...找不到别的资料啊只有别人的$Blog$ 试验了好多写法 貌似其中有好多带$Bug$的写法 最终定下了现在的版本应该是没大有问题的吧...明天再做两道虚树,

BZOJ 2286 树链剖分+DFS序+虚树+树形DP

第一次学习虚树,就是把无关的点去掉.S里维护一条链即可. 1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <algorithm> 5 #define LL long long 6 using namespace std; 7 const LL Maxm=501000; 8 const LL Maxn=250100; 9 const LL Inf=1e60; 1

BZOJ 2286 消耗战 (虚树+树形DP)

给出一个n节点的无向树,每条边都有一个边权,给出m个询问,每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接.最少的边权和是多少.(n<=250000,sigma(ki)<=500000) 考虑树形DP,我们令mn[i]表示i节点无法与1节点相连切除的最小权值.显然有mn[i]=min(E(fa,i),mn[fa]).大致就是i到1的简单路径上的最小边.我们对于每个询问.把询问的点不妨称为关键点.令dp[i]表示i节点不能与子树的关键点连接切掉的最小权值.那么有,如果son[i]

BZOJ 2286 SDOI2011 消耗战 倍增LCA+单调栈

题目大意:给定一棵树,边上有边权,m次询问,每次选定一些关键点,求将1号节点与所有关键点都切断所需的最小花销 关键点的总数<=50W 首先我们考虑暴力想法 令f[x]表示切断以x为根的子树中所有关键点的最小花销 g[x]表示x是不是关键点 那么对于x的每个子节点y有f[x]=Σmin(g[y]?INF:f[y],Distance(x,y) ) 这样每次暴力做一遍树形DP,时间复杂度是O(n*m)的 现在由于每次询问的点数不一定会达到n的级别,对所有节点进行DFS非常浪费 我们可以将询问的关键点拿

BZOJ 2286 [Sdoi2011]消耗战

题解:对询问点建立虚树 然后在虚树上Dp 每个点父边边权为这个点到根的边权最小值 一开始学了假的虚树 一开始竟然没想到父边边权可以这样赋 #include<iostream> #include<cstdio> #include<cstring> #include<vector> #include<queue> #include<algorithm> using namespace std; const int maxn=500009;