2286: [Sdoi2011消耗战

Time Limit: 20 Sec  Memory Limit: 512 MB
Submit: 1815  Solved: 645
[Submit][Status][Discuss]

Description

在一场战争中,战场由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

Sample Output

HINT

对于100%的数据,2<=n<=250000,m>=1,sigma(ki)<=500000,1<=ki<=n-1

对于每次查询,如果用一次树型dp就能得出结果。

dp方程:

f[father]+=fmin(g[son]?inf:f[son],(min_e(son,father));(g[i]标记是否是关键点)

这个时间效率很直观O(m*n)

每次查询,我们只需要遍历关键点与关键点之间的lca,其它点时可忽略的或可跳跃的。

那么就需要用到虚树的技巧了,虚树就是通过维护一个单调栈把树的关键点和它们之间的lca按照dfs序遍历一遍,遍历的过程中通过单调栈的调整来理清树的父亲和儿子之间的关系。

首先,对树节点进行dfs。在期间对节点进行标号dfn。

然后,维护一个单调栈。这个单调栈的节点都在一条链上。

对于栈顶元素 p,栈次顶元素 q, 即将插入节点x 有如下关系:

1.lca是p.此时dfn(x)>dfn(p)=dfn(lca)

这说明 x在p的下面,直接把x入栈即可

2.p和x分立在lca的两棵子树下.此时 dfn(x)>dfn(p)>dfn(ilca)

这时候就有三种讨论了

针对这道题的连边就是树型dp处理

(1)如果dfn(q)>dfn(lca),可以直接连边q->p,然后退一次栈.

(2)如果dfn(q)=dfn(lca),说明q=lca,直接连边lca->p,把p退栈,此时子树已经构建完毕.

(3)如果dfn(q)<dfn(lca),说明lca被p与q夹在中间,此时连边lca->q,把p退栈,再把lca压入栈.此时子树构建完毕.

重复判断上述过程

这里处理  min_e(p,q) p到q的路径中权值最小的边。需要用倍增lca或者树剖也是可以的。这个参见《挑战程序设计竞赛》吧,改改代码就可以了

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <vector>
#include <algorithm>
#define find_min(a,b) a>b?b:a
#define MAX_V 250008
#define MAX_LOG_V 21
#define INF 0x3f3f3f3f
#define inf (1ll<<40)
using namespace std;
typedef long long int ll;
struct edge{int to,cost;
};
vector<edge> G[MAX_V];
int par[MAX_LOG_V][MAX_V];//v节点向上走2^k步走到的节点
int mng[MAX_LOG_V][MAX_V];//v节点向上走2^k步中路过最小的边
int dfn[MAX_V];//每个点的dfs序标号
int dep[MAX_V];//深度
ll f[MAX_V];//树型dp
struct node{int h,dfn;
}hs[MAX_V];
bool g[MAX_V];//是否是关键点
int sta[MAX_V*2];// 模拟栈
int icnt;//栈顶 、id
int swap(int &x,int &y)
{//交换 x=x^y;y=x^y,x=x^y;
}
//当前点、父亲节点 、深度、连接父亲点的边权
void dfs(int v,int p,int d,int pre_e)
{//lca搜索预处理 par[0][v]=p,dep[v]=d,mng[0][v]=pre_e,dfn[v]=icnt++;for(int i=0;i<G[v].size();++i)if(G[v][i].to!=p) dfs(G[v][i].to,v,d+1,G[v][i].cost);
}
void init_tree()
{//预处理lca查询 icnt=0;//id初始化为0 dfs(1,-1,0,INF); for(int k=0;k+1<MAX_LOG_V;++k)for(int v=1;v<=MAX_V;++v){if(par[k][v]<0)//超过树根 par[k+1][v]=-1,mng[k+1][v]=INF;else{//能前进 2^(k+1)步 int u=par[k][v]; par[k+1][v]=par[k][u]; mng[k+1][v]=find_min(mng[k][v],mng[k][u]);}}
}
int lca(int u,int v)
{if(dep[u]>dep[v]) swap(u,v);//先到同一深度 for(int k=0;k<MAX_LOG_V;++k)if((dep[v]-dep[u])>>k & 1)v=par[k][v];if(u==v) return u;//同时向上 二分查询 for(int k=MAX_LOG_V-1;k>=0;--k)if(par[k][u]!=par[k][v])u=par[k][u],v=par[k][v];return par[0][u];
}
int min_e(int u,int v)
{int ilca=lca(u,v);int res=INF;//u->lca int mov;if(dep[ilca]<dep[u]){mov=dep[u]-dep[ilca];for(int k=0;k<MAX_LOG_V;++k)if(mov>>k &1)res=find_min(res,mng[k][u]),u=par[k][u];}//v->lca if(dep[ilca]<dep[v]){mov=dep[v]-dep[ilca];for(int k=0;k<MAX_LOG_V;++k)if(mov>>k &1)res=find_min(res,mng[k][v]),v=par[k][v];}return res;
}
void add_edge(int u,int v,int c)
{ G[u].push_back((edge){v,c}); G[v].push_back((edge){u,c});
}
void init()
{//初始化边数置0 for(int i=0;i<MAX_V;++i)G[i].clear();
}
int cmp(const void *a,const void *b)
{return ((node *)a)->dfn-((node *)b)->dfn;
}
ll fmin(ll a,ll b)
{return a>b?b:a;
}
void solve(int k)
{for(int i=1;i<=k;++i){int o=hs[i].h;hs[i].dfn=dfn[o];//同步搜索序id}qsort(hs+1,k,sizeof(hs[0]),cmp);int tp=0;sta[tp]=0;sta[++tp]=1;f[1]=0,g[1]=0;for(int i=1;i<=k;++i){int p=sta[tp],q=sta[tp-1],x=hs[i].h;int ilca=lca(p,x);while(dfn[p]>dfn[ilca]){if(dfn[q]<=dfn[ilca]){int tmp=fmin(g[tp]?inf:f[tp],(ll)min_e(p,ilca));sta[tp--]=0;if(ilca!=q)sta[++tp]=ilca,f[tp]=0,g[tp]=0;f[tp]+=tmp;break;}else{f[tp-1]+=fmin(g[tp]?inf:f[tp],(ll)min_e(p,q));sta[tp--]=0;}p=sta[tp],q=sta[tp-1];}if(sta[tp]!=x)sta[++tp]=x,f[tp]=0;g[tp]=1;}while(tp>1){int p=sta[tp],q=sta[tp-1];f[tp-1]+=fmin(g[tp]?inf:f[tp],(ll)min_e(p,q));sta[tp--]=0;}printf("%lld\n",f[tp--]);
}
int main()
{int n;while(~scanf("%d",&n)){init();int u,v,w;for(int i=0;i<n-1;++i){scanf("%d%d%d",&u,&v,&w);add_edge(u,v,w);}init_tree();int m,k,h;scanf("%d",&m);while(m--){scanf("%d",&k);for(int i=1;i<=k;++i)scanf("%d",&hs[i].h);solve(k);}}return 0;
}

bzoj-2286 消耗战【虚树+倍增lca+单调栈】相关推荐

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

    给出一个n节点的无向树,每条边都有一个边权,给出m个询问, 每个询问询问ki个点,问切掉一些边后使得这些顶点无法与顶点1连接. 最少的边权和是多少. (n<=250000,sigma(ki)&l ...

  2. 【bzoj3879】SvT 后缀数组+倍增RMQ+单调栈

    题目描述 (我并不想告诉你题目名字是什么鬼) 有一个长度为n的仅包含小写字母的字符串S,下标范围为[1,n]. 现在有若干组询问,对于每一个询问,我们给出若干个后缀(以其在S中出现的起始位置来表示), ...

  3. 洛谷_2495 [SDOI2011]消耗战(虚树)

    消耗战 题目链接:https://www.luogu.com.cn/problem/P2495 题解: 对于单样例,可以考虑树形DP. 但此题是多实例,所以需要对树进行处理,每次询问有k+1(加上一号 ...

  4. P3233-[HNOI2014]世界树【虚树,倍增】

    正题 题目链接:https://www.luogu.com.cn/problem/P3233 题目大意 nnn个点的一棵树,mmm次选出一些点作为关键点.每个树上的点会对最近的关键点做贡献,求每个关键 ...

  5. UOJ#271. 【清华集训2016】连通子树(虚树+倍增)

    传送门 题解: 注意到每种颜色个数比较少,于是建出虚树后暴力背包,用倍增维护一下虚链上的DP值即可. 所以说你只需要5个倍增数组和一些卡常技巧加上无数的小细节就可以通过这道题了. 不说了我去睡觉了. ...

  6. 「Luogu2495」 [SDOI2011]消耗战 虚树

    Luogu P2495 [SDOI2011]消耗战 problem Solution 苦思冥想稍作思考之后可以得到一个树形DP的方法: 令\(w(u,v)\)表示u,v之间的边的权值,\(f[u]\) ...

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

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

  8. P2495 [SDOI2011]消耗战 虚树入门

    一棵树,n个点m个操作.每条边有权值,每个操作给你k个点,问断开若干条边后使k个点与根不相连的最小边权和是多少. 有sigmaK<500000 易知一个裸的树dp需要复杂度,m次操作后总复杂度为 ...

  9. P2495 [SDOI2011]消耗战-虚树+树形dp

    https://www.luogu.com.cn/problem/P2495 虚树:当我们在解决树形dp的问题的时候,题目中会给出一些询问,询问涉及的关键节点不多,并保证总的点数规模的时候,我们就可以 ...

  10. 洛谷 P4660 bzoj 1168 [ Baltic OI 2008 ] 手套 —— 分析+单调栈

    题目:https://www.luogu.org/record/show?rid=12702916 https://www.lydsy.com/JudgeOnline/problem.php?id=1 ...

最新文章

  1. linux--memcache的安装和使用(转)
  2. java托盘图标变白在linux,为什么用java实现windows系统托盘图标总是不显示出来
  3. 学习笔记Hive(七)—— 自定义函数
  4. ASP.NET知识集
  5. 奔跑吧兄弟变成机器人是哪一期_奔跑吧预告,郑恺郭麒麟回归,而我却被女嘉宾的颜值吸引了...
  6. 七阶拉丁方阵_【C语言】输出N阶拉丁方阵并统计个数
  7. Java程序员已经饱和了,还有必要培训Java编程嘛
  8. Missing iOS Distribution signing identity for …
  9. 力学 —— 物体固有属性之惯性张量矩阵
  10. USSD设置呼叫转移功能
  11. Java类加载器深入探索
  12. 如何制定软件开发计划
  13. 中国移动发狠,给携转用户巨额优惠,反击中国电信
  14. 差距不止一星半点,Github星标85K的性能优化法则圣经
  15. php递归函数return问题
  16. centos tcpdump
  17. 【阅读】A Comprehensive Survey on Electronic Design Automation and Graph Neural Networks——EDA+GNN综述翻译
  18. LeetCode 826 Most Profit Assigning Work
  19. 互联网大佬们齐聚首,除了演讲还有啥?点击All in 全部热点!
  20. 学在浙大只有查看权限的课件下载

热门文章

  1. html怎么设置整体右对齐,如何在html中右对齐按钮
  2. html如何添加竖虚线,在word中添加竖虚线的方法
  3. 2018年博客之星,需要您宝贵的一票!
  4. CSS 实现 系统登录界面 (二)
  5. 电容器充放电的原理是什么?
  6. 为什么买域名必须实名认证?这样做什么原因?
  7. 2021-2027全球与中国铂金芯片温度传感器市场现状及未来发展趋势
  8. LaTeX2021 公式编写、图文安装、详细教程、一文读懂
  9. oracle优化日记脚本
  10. ios根号怎么打_ios计算器开根号 苹果手机计算器怎么开根号 详情介绍