BZOJ2286: [Sdoi2011]消耗战(虚树)
BZOJ2286: [Sdoi2011]消耗战
Time Limit: 20 Sec
Memory Limit: 512 MBDescription
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望。已知在其他k个岛屿上有丰富能源,为了防止敌军获取能源,我军的任务是炸毁一些桥梁,使得敌军不能到达任何能源丰富的岛屿。由于不同桥梁的材质和结构不同,所以炸毁不同的桥梁有不同的代价,我军希望在满足目标的同时使得总代价最小。
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到1号岛屿上)。不过侦查部门还发现了这台机器只能够使用m次,所以我们只需要把每次任务完成即可。
Input
第一行一个整数n,代表岛屿数量。
接下来n-1行,每行三个整数u,v,w,代表u号岛屿和v号岛屿由一条代价为c的桥梁直接相连,保证1<=u,v<=n且1<=c<=100000。
第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,sigma(ki)<=500000,1<=ki<=n-1
题目地址: BZOJ2286: [Sdoi2011]消耗战
题目大意: 已经很简洁了
题解:
裸的虚树
虚树的概念
虚树,就是在有一棵树的情况下,对于数量较少的点进行询问时所建的一棵新的树,虚树包含询问的点和询问的点的lca(最近公共祖先),上面的点被称为关键点。对于两个关键点A,B,它们的连边上包含着原本树上两点之间那条链上的关键信息(这个信息可以是边权最大值、边权最小值、或者是边权之和,这个取决于实际需要),然后就可以进行树形dp了,这样的复杂度是基于询问的点数的,就可以想象虚树就是把一棵大树浓缩成一棵拥有所有你需要的信息的小树。
建树的方法
那么怎么建树呢,比较常见的做法是维护一个栈,里面存储着一条链,每一次加入一个点进行操作。
具体列举一下步骤吧
预备知识:
用较优的复杂度求lca,以及求两点之间的距离,一般倍增做,或者树链剖分加前缀和都可以,这都是单次O(logn)的,另外呢,要事先求好原树的dfs序和每个点的深度(这里的深度是指点序的深度,在计算这个深度的时候每条边都是为1来算),后面要用
询问操作:
1.输入每个询问的点,并且按照dfs序为关键字排序
2.将第1个点压到栈当中,开始构建虚树
3.枚举到下一个点u,计算u与栈顶点v的公共祖先lca
4.假设栈中栈顶下方的点为w(若栈中只有1个点就直跳过这一步),若w点的深度大于lca就把v向w连一条边,并且弹掉v,重复此步,否则就到下一步
5.若lca不是当前的v,那么就把lca和v连边,把v弹出,让lca成为栈顶元素(注:这个操作的意思是如果栈顶没有这个lca那么就压入),否则不做任何操作
6.最后把u压入栈中
7.回到3操作枚举下个点,直到枚举完了所有点
8.把栈顶v与栈顶下方的点为w连边,并且把v弹掉,这么做直到栈里只有一个点
9.栈里剩下的点就是虚树的根了
接下来你就可以开始进行dp等操作了
虚树的复杂度
虚树的建树的复杂度是O(k∗log(n))的,树形dp就是O(k)的啦,因为考虑最后虚树上的关键点有询问的点,和lca,然后每个询问的点最多产生1个新的lca,所以复杂度就是对的啦
来自https://blog.csdn.net/zhouyuheng2003/article/details/79110326
大神的博客里写的很清楚了,具体看代码实现吧:)
此题很容易发现用 dp 解决
但是多组询问 \(O(nm)\) 会TLE
重新建图把点缩少再跑 dp 就好了
具体看代码实现吧:)
AC代码
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
using namespace std;
const int N=250005;
const ll inf=5e10;
int n,Q,K,top,ind;
int a[N],h[N];
int cnt,_cnt,last[N],_last[N];
inline int read(){int x=0;char ch=getchar();while(ch<'0'||ch>'9')ch=getchar();while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x;
}
struct edge{int to,val,next;
}e[N<<1];
struct _edge{int to,next;
}_e[N];
void add_edge(int u,int v,int w){e[++cnt]=(edge){v,w,last[u]};last[u]=cnt;e[++cnt]=(edge){u,w,last[v]};last[v]=cnt;
}
void _add_edge(int u,int v){if(u==v)return;_e[++_cnt]=(_edge){v,_last[u]};_last[u]=_cnt;
}
int pos[N],dep[N],fa[N][20];
ll mn[N];
void dfs(int u){pos[u]=++ind;for(int i=last[u];i;i=e[i].next){int v=e[i].to;if(v==fa[u][0])continue;fa[v][0]=u;dep[v]=dep[u]+1;mn[v]=min(mn[u],(ll)e[i].val);dfs(v);}
}
int lca(int a,int b){if(dep[a]<dep[b])swap(a,b);for(int i=18;i>=0;i--)if(dep[fa[a][i]]>=dep[b])a=fa[a][i];for(int i=18;i>=0;i--)if(fa[a][i]!=fa[b][i])a=fa[a][i],b=fa[b][i];if(a==b)return a;return fa[a][0];
}
bool cmp(int a,int b){return pos[a]<pos[b];
}
ll f[N];
void DP(int u){f[u]=mn[u];ll tmp=0;for(int i=_last[u];i;i=_e[i].next){int v=_e[i].to;DP(v);tmp+=f[v];}_last[u]=0;if(tmp!=0)f[u]=min(f[u],tmp);
}
int q[N];
void solve(){_cnt=0;K=read();for(int i=1;i<=K;i++)h[i]=read();sort(h+1,h+K+1,cmp);int tot=0;h[++tot]=h[1];for(int i=2;i<=K;i++)if(lca(h[tot],h[i])!=h[tot])h[++tot]=h[i];top=0;q[++top]=1;for(int i=1;i<=tot;i++){int now=h[i],F=lca(now,q[top]);while(1){if(dep[F]>=dep[q[top-1]]){_add_edge(F,q[top--]);if(q[top]!=F)q[++top]=F;break;}_add_edge(q[top-1],q[top]);top--;}if(q[top]!=now)q[++top]=now;}while(--top)_add_edge(q[top],q[top+1]);DP(1);printf("%lld\n",f[1]);
}
int main(){n=read();for(int i=1;i<n;i++){int u=read(),v=read(),w=read();add_edge(u,v,w);}mn[1]=inf;dep[1]=1;dfs(1);for(int j=1;j<=18;j++)for(int i=1;i<=n;i++)fa[i][j]=fa[fa[i][j-1]][j-1];Q=read();while(Q--)solve();return 0;
}
作者:skl_win
出处:https://www.cnblogs.com/shaokele/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
转载于:https://www.cnblogs.com/shaokele/p/9507627.html
BZOJ2286: [Sdoi2011]消耗战(虚树)相关推荐
- 「Luogu2495」 [SDOI2011]消耗战 虚树
Luogu P2495 [SDOI2011]消耗战 problem Solution 苦思冥想稍作思考之后可以得到一个树形DP的方法: 令\(w(u,v)\)表示u,v之间的边的权值,\(f[u]\) ...
- 洛谷_2495 [SDOI2011]消耗战(虚树)
消耗战 题目链接:https://www.luogu.com.cn/problem/P2495 题解: 对于单样例,可以考虑树形DP. 但此题是多实例,所以需要对树进行处理,每次询问有k+1(加上一号 ...
- 洛谷 P2495 [SDOI2011]消耗战 虚树
题目描述 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知 ...
- P2495 [SDOI2011]消耗战 虚树入门
一棵树,n个点m个操作.每条边有权值,每个操作给你k个点,问断开若干条边后使k个点与根不相连的最小边权和是多少. 有sigmaK<500000 易知一个裸的树dp需要复杂度,m次操作后总复杂度为 ...
- P2495 [SDOI2011]消耗战-虚树+树形dp
https://www.luogu.com.cn/problem/P2495 虚树:当我们在解决树形dp的问题的时候,题目中会给出一些询问,询问涉及的关键节点不多,并保证总的点数规模的时候,我们就可以 ...
- BZOJ2286 : [Sdoi2011]消耗战
对于每次询问,构造出虚树,相邻两点边权为该两点路径上边权的最小值 f[i]表示以i为根的子树与1不连通的最小代价,vip[i]表示i是不是关键点 f[i]=sum(vip[j]?w[j]:min(f[ ...
- BZOJ 2286 消耗战 (虚树+树形DP)
给出一个n节点的无向树,每条边都有一个边权,给出m个询问, 每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接. 最少的边权和是多少. (n<=250000,sigma(ki)&l ...
- bzoj2286 [Sdoi2011]消耗战 单调栈+lca
这个题意思非常明确,就是删除尽量小的边使1和指定点不连通 对于一次询问,dfs一遍即可,但需要log级别的 然后条件里有个Σ,显然复杂度和他有关,对每个点都需要一个均摊log的 然后有两个基本思路, ...
- P2495 [SDOI2011]消耗战
P2495 [SDOI2011]消耗战 虚树+dpdpdp. 每次在整个图上跑dpdpdp时间肯定不够,所以考虑只对查询点和其lcalcalca进行建图. 此题的dpdpdp转移有两种方式:一种是直接 ...
最新文章
- 组策略分发软件全攻略
- 如何快速学会别人的代码和思维
- shell中$后加引号有什么用($string和$'string')
- Oracle创建dblink和同义词小记
- IDEA设置虚拟机参数
- springboot2.5.0 整合 redis 配置详解
- Android Studio开发基础之AutoCompleteTextView控件的使用
- 数组的应用 冒泡排序
- 传统服务器性能不足 解决方案,云平台和传统服务器的优劣
- (100)FPGA单沿和双沿采样(下降沿采样)
- 力扣441.排列硬币
- c++知识整理 编程模块
- 使用若依前后端分离下载需要授权的url文件
- 网页设计如何排成一列_学习DIV+CSS网页布局之一列布局
- 智能开关如何实现双控
- android蓝牙传输文件到mysql_使用Android将图像发送到MySQL数据库
- 影魔[AH2017/HNOI2017]
- javaScript进阶webAPI web前端api进阶DOM、BOM学习笔记day01
- c语言用循环输出塔状五行,从键盘上输入一个正整数n,请按照以下五行杨辉三角形的显示方式, 输出杨辉三角形的前n行。请采用循环控制语句来实现。...
- 720全景图制作的方法,旅游业拥有全景的优势