先看一道题:

【BZOJ2286】【SDOI2011】消耗战

Description

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

侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。

Input

第一行一个整数n,代表岛屿数量。

接下来n−1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连。

第n+1行,一个整数m,代表敌方机器能使用的次数。

接下来m行,每行一个整数ki,代表第i次后,有ki个岛屿资源丰富,接下来k个整数h1,h2,…hk,表示资源丰富岛屿的编号。

Output

输出有m行,分别代表每次任务的最小代价。

Sample Input

10

1 5 13

1 9 6

2 1 19

2 4 8

2 3 91

5 6 8

7 5 4

7 8 31

10 7 9

3

2 10 6

4 5 7 8 3

3 9 4 6

Sample Output

12

32

22

HINT

对于100%的数据,2≤n≤250000,m≥1,∑ki≤500000,1≤ki≤n−1,1≤u,v≤n,1≤c≤100000

可以想到一个树形DP,\(dp[i]\)表示切断在以i为根的子树中所有关键点(即题中资源丰富的岛屿)所需的最小代价。

这样的树形DP跑一次当然是\(O(n)\)的,总复杂度为\(O(NM)\),肯定过不了。

但是模拟一下可以发现,有一些点的dp值是完全对答案没有贡献的(例如子树中不包含关键点),但是却要计算它的dp值,这样就增加了算法的时间复杂度。

那这样可以思考一下,能不能去掉这些没有用的点,使得树形DP的树尽量小,从而达到减少时间复杂度的效果?

显然可以。

记录一个从每一个点到根路径上权值最小的边,这时对答案真正有贡献的是所有关键点以及它们的LCA,以及它们LCA的LCA……,将这些节点抽出来,加上一个1号节点为根,再在这颗新建的树上面跑DP即可。

然而实际上可以不用连边,记录每个点在dfs里面进出栈的顺序,每个点变成表示出栈和进栈的点,拿一个栈进行模拟即可,顺带可以清空。

这样的时间复杂度是多少?

因为每次本身有关键节点,再加上LCA,总空间复杂度是\(O(2*∑ki)\),时间复杂度是\(O(M{log_{2*∑ki}})\)

\(----------------------------------------------\)

2019.11.30:

做了几道题后发现像上面这样做会在需要多次便利时很麻烦,而且因为没用树剖求LCA,所以搞得程序很慢,所以换一种建虚树的方法。

同样的,我们是要求出每个点的dfn序,将所有节点按照dfn序排列。

然后用一个栈来维护根到栈顶元素的路径上的点。

假设新加入一个节点p,栈顶元素为\(st[top]\),lca为x和p的最近公共祖先。

因为是按照dfn序排列,所以p不是lca,

1.如果x是lca,直接将p入栈即可。

2.如果x不是lca,则p和x不在同一棵子树中,且x的子树已经遍历完了(因为x的子树中若有y未被遍历,那么y一定在p之前被遍历)

开始构建:

设栈顶中第二个元素为y,分三种情况讨论:

1.dfn[y]>dfn[lca]

连y到x的边(y先放肯定y在x上面),x出栈

2.dfn[y]==dfn[lca]

y等于lca。

连lca到x的边,子树的构建完成。

3.dfn[y]<dfn[lca]

lca在y和x之间,连lca到x的边,x出栈,lca入栈

最后将p入栈即可。

不断重复进行这个过程,就可以构建虚树了。

就给个构建树的函数吧

void insert(int x)
{
if(top==1)
{
st[++top]=x;
return;
}
int LCA=lca(x,s[top]);
if(LCA==s[top])
{
st[++top]=x;
return;
}
while(top>1&&in[st[top-1]]>=in[LCA])
{
adde1(st[top-1],st[top]);
top--;
}
if(LCA!=st[top])
{
adde1(LCA,st[top]);
st[top]=LCA;
}
st[++top]=x;
}

下面的代码就用栈模拟吧。

#include<bits/stdc++.h>
#define N 500010
#define inf 10000010
using namespace std;
int in[N],to[N<<1],nxt[N<<1],cost[N<<1],head[N],out[N],ans[N],deep[N],fa[N][20],indew,cnt,nct,n,m,x,y,z,tree[N];
long long dp[N];
bool is[N];
stack<int> s;
void adde(int x,int y,int z)
{
to[++cnt]=y;
nxt[cnt]=head[x];
cost[cnt]=z;
head[x]=cnt;
}
void dfs(int u)
{
for(int i=1;i<=19;i++)
{
fa[u][i]=fa[fa[u][i-1]][i-1];
}
for(int i=head[u];i;i=nxt[i])
{
int v=to[i],w=cost[i];
if(!deep[v])
{
in[v]=++indew;//进栈序
deep[v]=deep[u]+1;
ans[v]=min(ans[u],w);
fa[v][0]=u;
dfs(v);
}
}
out[u]=++indew;//出栈序
}
bool cmp(int x,int y)//按dfn序进行排序
{
return ((x>0)?in[x]:out[-x])<((y>0)?in[y]:out[-y]);
}
int lca(int x,int y)
{
if(deep[x]<deep[y])
{
swap(x,y);
}
for(int i=19;i>=0;i--)
{
if(deep[fa[x][i]]>=deep[y])
{
x=fa[x][i];
}
}
if(x==y)
{
return x;
}
for(int i=19;i>=0;i--)
{
if(fa[x][i]!=fa[y][i])
{
x=fa[x][i];
y=fa[y][i];
}
}
return fa[x][0];
}
int main()
{
// freopen("1.txt","r",stdin);
scanf("%d",&n);
for(int i=1;i<n;i++)
{
scanf("%d%d%d",&x,&y,&z);
adde(x,y,z);
adde(y,x,z);
}
ans[1]=inf;
deep[1]=1;
dfs(1);
scanf("%d",&m);
while(m--)
{
nct=0;
scanf("%d",&x);
while(x--)//以下是建虚树
{
scanf("%d",&tree[++nct]);
dp[tree[nct]]=ans[tree[nct]];
is[tree[nct]]=1;
}
sort(tree+1,tree+nct+1,cmp);//先按dfn排序
for(int i=2;i<=nct;i++)//放LCA进树中
{
int LCA=lca(tree[i],tree[i-1]);
if(!is[LCA])
{
tree[++nct]=LCA;
is[LCA]=1;
}
}
int nct1=nct;
for(int i=1;i<=nct1;i++)//加出栈点
{
tree[++nct]=-tree[i];
}
if(!is[1])
{
tree[++nct]=1;
tree[++nct]=-1;
is[1]=1;
}
sort(tree+1,tree+nct+1,cmp);
for(int i=1;i<=nct;i++)//用栈模拟
{
if(tree[i]>0)
{
s.push(tree[i]);
}else{
int v=s.top(),u;//v是儿子,v出栈后,u就是父亲
s.pop();
if(v==1)
{
printf("%lld\n",dp[1]);
break;
}
u=s.top();
dp[u]+=min(dp[v],1ll*ans[v]);//dp
dp[v]=is[v]=0;
}
}
}
return 0;
}

虚树+【BZOJ2286】【SDOI2011】消耗战(虚树)(DP)的更多相关文章

  1. [BZOJ2286][SDOI2011]消耗战(虚树DP)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4998  Solved: 1867[Submit][Statu ...

  2. BZOJ2286: [Sdoi2011]消耗战(虚树/树形DP)

    Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 5246  Solved: 1978[Submit][Status][Discuss] Descript ...

  3. bzoj2286: [Sdoi2011]消耗战 虚树

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

  4. BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB Submit: 4261  Solved: 1552 [Submit][Sta ...

  5. 【BZOJ2286】[Sdoi2011]消耗战 虚树

    [BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的 ...

  6. 【BZOJ-2286】消耗战 虚树 + 树形DP

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2120  Solved: 752[Submit][Status] ...

  7. bzoj 2286: [Sdoi2011]消耗战 虚树+树dp

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...

  8. [Bzoj2286][Sdoi2011]消耗战(虚树模板题附讲解)

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 4896  Solved: 1824[Submit][Statu ...

  9. 【学习笔记】虚树复习记(BZOJ2286 SDOI2011 消耗战)

    想写战略游戏却想不起来虚树T^T 所以就有了这篇复习记QwQ ——简介!—— 我们在处理树上问题的时候,dfs是一个常用手段,但是我们发现,如果一棵树上只有一部分关键点,每次dfs需要访问好多不是关键 ...

  10. BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp 动态规划 dfs序

    https://www.lydsy.com/JudgeOnline/problem.php?id=2286 wa了两次因为lca犯了zz错误 这道题如果不多次询问的话就是裸dp. 一棵树上多次询问,且 ...

随机推荐

  1. 51 Nod 1509 加长棒(巧妙的隔板法计数)

    1509 加长棒  题目来源: CodeForces 基准时间限制:1 秒 空间限制:131072 KB 分值: 40 难度:4级算法题  收藏  关注 现在有三根木棒,他们的长度分别是a,b,c厘米 ...

  2. [Luogu] 花神游历各国

    https://www.luogu.org/problemnew/show/P4145 线段树区间求和 + 区间开根号 对1e9的数开根号下取整用不了几次就会<=1 因此暴力开根号,记录区间最大 ...

  3. 【线性代数】2-7:转置与变换(Transposes and Permutation)

    title: [线性代数]2-7:转置与变换(Transposes and Permutation) toc: true categories: Mathematic Linear Algebra d ...

  4. c语言if-else的效率比较

    闲着没事测试下if-else的执行效率 测试环境:Mac pro i7 2.3Ghz ...编译器gcc 4.9,代码没有进行优化-O0: 测试代码:c代码1: int main(){ int n=1 ...

  5. 关于centos6版本执行程序报错:libc.so.6: version GLIBC_2.14 not found的解决

    执行后程序报错: libc.so.6: version GLIBC_2.14 not found 这种情况是因为当前服务器glibc的版本比较低造成的(不出意外是glibc_2.12是最高版本): 1 ...

  6. Jmeter工具做接口测试

    一.安装Jmeter 1.安装JDK ①下载jdk,到官网下载jdk,地址:http://jmeter.apache.org/download_jmeter.cgi ② 安装jdk(Oracle官网下 ...

  7. nginx静态页面镜像

    user root; worker_processes ; #error_log logs/error.log; #error_log logs/error.log notice; #error_lo ...

  8. 1.5 JAVA的IO操作

    1.5 JAVA的IO操作 参考链接:https://www.runoob.com/java/java-files-io.html 一.JAVA的IO操作 由于JAVA引用外界的数据,或是将自身的数据 ...

  9. IDEA出现Could not autowire. No beans of 'xxx' type found.解决

    Plan A File → Project Structure... Facets → Spring → 右键删除即可 Plan B File → Settings → Editor → Inspec ...

  10. java日期大小比较

    之前有面试到两个日期的大小比较方式,现在整理一下几种方法. 例子: String beginTime=new String("2017-06-09 10:22:22");    S ...