题目

我们可以很快的想到一个单次\(O(n)\)的dp。

然后我们注意到这个dp有很多无用的操作,比如一条没有关键点的链可以直接去掉。

所以我们可以尝试一次dp中只管那些有用的点。

题目给的关键点显然是要算进去的,但是只算这些的我们不足以构成一棵树,所以我们还需要任意两点的lca。

如果我们把关键点排序,那么我们需要的就是相邻两个点的lca。

这样我们就把需要的点给拿了出来,显然单次询问点数是\(k\)级别的。

但是我们还需要一个方法来让我们把这\(k\)个点给建一棵树(也就是掌握它们的相对关系),不然我们没办法做树形dp。

建出来的这个树叫做虚树,接下来我们将给出一个构建虚树的方法。

(其实对于一棵较小的树而言,手玩构造虚树是十分容易的,但是我们需要用程序化的语言来表达它)

我们知道建树的过程是一个dfs,而dfs的实现是栈,所以我们用栈来模拟建树。

按dfs序枚举每个点,假设当前点是\(p\),栈顶元素是\(x\),第二个元素是\(y\)。

首先如果栈大小只有\(1\)即没有\(y\)元素的话我们可以直接把\(p\)入栈。

然后我们求出\(p,x\)的lca,记为\(l\)。

如果\(l=x\),即\(p\)在\(x\)的子树中的话,我们就直接把\(p\)入栈。(在这一道题可以不用入栈,因为覆盖一个点一定会覆盖它的子树中的所有点)

否则我们再考虑\(l\)与\(y\)的关系。

如果\(dfn_l\le dfn_y\),即那么此时\(y\)在\(l-x\)的链上且为\(x\)的“父亲”(这个父亲指的是虚树中的父亲)(如果单纯只有这个限制条件的话,还有可能\(y\)并不在\(l-x\)链上,但是根据我们之后的一些操作,我们会保证这个栈里面的节点一定是一条链,这样的话\(y\)就一定会在\(l-x\)的链上了。),那么我们就把\(x\)退栈并且加一条\((y,x)\)的边。然后重复这个操作。

直到\(dfn_y\ge dfn_l\)了,我们再判断\(x,l\)的关系。

如果此时\(x=l\),那么我们就可以直接把\(p\)入栈。

否则我们连一条\((l,x)\)的边并把\(l\)入栈,再把\(p\)入栈。

看上去非常玄学的过程,自己画一棵树模拟一下会好理解很多,这东西太抽象了

这样我们就把单次dp的复杂度降到了\(O(k)\)。

然后根据实现lca的方式,我们有\(O(n\log n+\sum k\log\sum k),O(n\log n+\sum k),O(n+\sum k)\)的三种复杂度。

#include<bits/stdc++.h>
#define pi pair<int,int>
#define pb push_back
#define ll long long
using namespace std;
namespace IO
{
char ibuf[(1<<21)+1],obuf[(1<<21)+1],st[19],*iS,*iT,*oS=obuf,*oT=obuf+(1<<21);
char Get(){return (iS==iT? (iT=(iS=ibuf)+fread(ibuf,1,(1<<21)+1,stdin),(iS==iT? EOF:*iS++)):*iS++);}
void Flush(){fwrite(obuf,1,oS-obuf,stdout),oS=obuf;}
void Put(char x){*oS++=x;if(oS==oT)Flush();}
int read(){int x=0,c=Get();while(!isdigit(c))c=Get();while(isdigit(c))x=x*10+c-48,c=Get();return x;}
void write(ll x){int top=0;if(!x)Put('0');while(x)st[++top]=(x%10)+48,x/=10;while(top)Put(st[top--]);Put('\n');}
}using namespace IO;
ll min(ll a,ll b){return a<b? a:b;}
const int N=250007;const ll inf=1e18;int a[N];
namespace Graph
{
vector<pi>E[N];
int T,dfn[N],fa[N],size[N],tpa[N],son[N],dep[N];ll mn[N];
void add(int u,int v,int w){E[u].pb(pi(v,w));}
void dfs(int u)
{
size[u]=1,dep[u]=dep[fa[u]]+1,dfn[u]=++T;
for(auto [v,w]:E[u]) if(v^fa[u]) mn[v]=min(mn[u],w),fa[v]=u,dfs(v),size[u]+=size[v],son[u]=size[v]>size[son[u]]? v:son[u];
}
void dfs(int u,int tp)
{
tpa[u]=tp;
if(son[u]) dfs(son[u],tp);
for(auto [v,w]:E[u]) if(v^fa[u]&&v^son[u]) dfs(v,v);
}
int lca(int u,int v)
{
while(tpa[u]^tpa[v]) dep[tpa[u]]>dep[tpa[v]]? u=fa[tpa[u]]:v=fa[tpa[v]];
return dep[u]>dep[v]? v:u;
}
void build(){dfs(1),dfs(1,1);}
}using namespace Graph;
namespace ITree
{
int stk[N],top;vector<int>G[N];
void push(int u)
{
if(top==1) return (void)(stk[++top]=u);
int l=lca(u,stk[top]);
if(l==stk[top]) return;
while(top>1&&dfn[stk[top-1]]>=dfn[l]) G[stk[top-1]].pb(stk[top]),--top;
if(stk[top]^l) G[l].pb(stk[top]),stk[top]=l;
stk[++top]=u;
}
ll dp(int u)
{
if(!G[u].size()) return mn[u];
ll sum=0;
for(int v:G[u]) sum+=dp(v);
return G[u].clear(),min(mn[u],sum);
}
}using namespace ITree;
int main()
{
int n=read();
for(int i=1,u,v,w;i<n;++i) u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
mn[1]=inf,build();
for(int i,k,m=read();m;--m)
{
for(k=read(),i=1;i<=k;++i) a[i]=read();
sort(a+1,a+k+1,[](int i,int j){return dfn[i]<dfn[j];}),stk[top=1]=1;
for(i=1;i<=k;++i) push(a[i]);
while(top) G[stk[top-1]].pb(stk[top]),--top;
write(dp(1));
}
return Flush(),0;
}

Luogu P2495 [SDOI2011]消耗战的更多相关文章

  1. luogu P2495 [SDOI2011]消耗战 |虚树+LCA+dp

    题目描述 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知 ...

  2. 洛谷P2495 [SDOI2011]消耗战(虚树dp)

    P2495 [SDOI2011]消耗战 题目链接 题解: 虚树\(dp\)入门题吧.虚树的核心思想其实就是每次只保留关键点,因为关键点的dfs序的相对大小顺序和原来的树中结点dfs序的相对大小顺序都是 ...

  3. ●洛谷P2495 [SDOI2011]消耗战

    题链: https://www.luogu.org/problemnew/show/P2495题解: 虚树入门,树形dp 推荐博客:http://blog.csdn.net/lych_cys/arti ...

  4. P2495 [SDOI2011]消耗战 lca倍增+虚树+树形dp

    题目:给出n个点的树  q次询问  问切断 k个点(不和1号点联通)的最小代价是多少 思路:树形dp  sum[i]表示切断i的子树中需要切断的点的最小代价是多少 mi[i]表示1--i中的最小边权 ...

  5. P2495 [SDOI2011]消耗战

    思路 虚树上DP 虚树相当于一颗包含了所有询问的关键点信息的树,包含的所有点只有询问点和它们的LCA,所以点数是\(2k\)级别的,这样的话复杂度就是\(O(\sum k)\),复杂度就对了 虚树重点 ...

  6. 洛谷P2495 [SDOI2011]消耗战(虚树)

    题面 传送门 题解 为啥一直莫名其妙\(90\)分啊--重构了一下代码才\(A\)掉-- 先考虑直接\(dp\)怎么做 树形\(dp\)的时候,记一下断开某个节点的最小值,就是从根节点到它的路径上最短 ...

  7. [洛谷P2495][SDOI2011]消耗战

    题目大意:有一棵$n(n\leqslant2.5\times10^5)$个节点的带边权的树,$m$个询问,每次询问给出$k(\sum\limits_{i=1}^mk_i\leqslant5\times ...

  8. 洛谷 P2495 [SDOI2011]消耗战(虚树,dp)

    题面 洛谷 题解 虚树+dp 关于虚树 了解一下 具体实现 inline void insert(int x) { if (top == 1) {s[++top] = x; return ;} int ...

  9. P2495 [SDOI2011]消耗战 虚树

    这是我做的第一道虚树题啊,赶脚不错.其实虚树也没什么奇怪的,就是每棵树给你一些点,让你多次查询,但是我不想每次都O(n),所以我们每次针对给的点建一棵虚树,只包含这些点和lca,然后在这棵虚树上进行树 ...

随机推荐

  1. 初次使用自己写的testbench 验证了简单的NOT门。

    先是简单的非门模型: module notgate(a,b); input a; output b; assign b=~a; endmodule 下面是自己写的简陋的testbench: `time ...

  2. Python实用黑科技——解包元素(2)

    需求: 前面的文章讲的是使用变量的个数需要和迭代器数据变量的元素个数相同的方法,但更多的时候确实不想根据元素个数n来定义相应多的变量,而是希望用较少的变量( def drop_first_last(g ...

  3. HDU 4496 D-City —— (并查集的应用)

    给出n个点和m条边,一条一条地删除边,问每次删除以后有多少个联通块. 分析:其实就是并查集的应用,只是前一阵子一直做图论思路一直囿于tarjan了..方法就是,记录每一条边,然后从最后一条边开始不断的 ...

  4. CSS-居中方法汇总

    CSS居中是前端工程师经常要面对的问题,也是基本技能之一.今天有时间把CSS居中的方案汇编整理了一下,目前包括水平居中,垂直居中及水平垂直居中方案共15种.如有漏掉的,还会陆续的补充进来. 水平居中 ...

  5. 子类中执行父类的方法(引出super()与mro列表)

    1. 我们先想一下在python中如果子类方法中想执行父类的方法,有什么方式?大概有三种: Parent.__init__(self, name) # 通过父类的名字,指定调用父类的方法 super( ...

  6. Leetcode题目141.环形链表(简单)

    题目描述: 给定一个链表,判断链表中是否有环. 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始). 如果 pos 是 -1,则在该链表中没有环. 示例 ...

  7. 轻松搭建ES6开发环境

    首先,你要自行查阅什么是ES6和ES5.javascript有什么关系,为什么要编译ES6.废话不多说,just go! 第一步:创建项目并让它成为npm可以管理的仓库. 新建一个项目,名字假设为te ...

  8. leetcode探索高级算法

    C++版 数组和字符串 正文 链表: 正文 树与图: 树: leetcode236. 二叉树的最近公共祖先 递归(先序) leetcode124二叉树最大路径和 递归 图: leetcode 547朋 ...

  9. mysql查询json字段

    一张test表里存了一个content字段是json类型的,查询该content里manualNo这个字段 select JSON_EXTRACT (test .content, '$.manualN ...

  10. [Java读书笔记] Effective Java(Third Edition) 第2章 创建和销毁对象

      第 1 条:用静态工厂方法代替构造器 对于类而言,获取一个实例的方法,传统是提供一个共有的构造器. 类可以提供一个公有静态工厂方法(static factory method), 它只是一个返回类 ...