题目链接

题目大意:

给出一棵树,之后对这棵树进行q次询问,每次询问一个点集,给出使得这些点集中的点都不能直接或者间接的与1相连,需要删边的最小代价(边的代价在给树的时候给出),,点集不包含1,点集总共大小<=500000

题目思路:

首先考虑如果没有m次询问的结果

考虑树形dp

对于一个点来说:

1.如果其是询问点,那么此点必定会删除,所以这个点的贡献必然是1~u之间的边权最小值

2.如果不是询问点,那么对于这个点来说,可以全部删除其子树的点 同时也可以找一个 该点到1的边权最小值,断掉该点子树的所有联系

所以状态方程:

很明显,如果每次都这么做,复杂度就会到O(M*N)

时间浪费在了哪?

假设一个询问点是叶子节点,中间所有点的均为无用点,那么进行树形dp时一些点是没必要存在的,但是却遍历了

那么从这个角度出发,我们能不能使得每次的树都成为只包含询问点的树呢?

之后因为点集总和不超过500000那么复杂度就允许了。

所以,我们设法构造一个新树,使得新树中只包含任意两询问点的LCA和询问点

所以,用到的算法——虚树

所以,我们可以将虚树构造出来,之后只需要在虚树上进行树形dp即可

构造虚树时只需要注意几个情况:

1.首先按dfs序排序,确保祖先孩子的顺序,之后开一个栈记录遍历过程。

2.如果该点与栈顶元素的lca 相同,那么说明新来的点与栈顶点在同一条树链,直接入栈即可

3.如果该点与栈顶元素的lca不同,那么说明当前这些点以lca为分界点将这两点分在了不同的子树中,此时只需要不断的回退栈即可。

之后对于链上的最小值问题:我觉得既然倍增都用了,再用一个倍增也无妨,最后发现完全可以在过程中记录或者预处理,可以优化时间复杂度一下!

Code:

/*** keep hungry and calm CoolGuang!***/
#pragma GCC optimize(2)
//#include <bits/stdc++.h>
#include<stdio.h>
#include<queue>
#include<algorithm>
#include<string.h>
#include<iostream>
#define debug(x) cout<<#x<<":"<<x<<endl;
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int,int> pp;
const ll INF=1e17;
const int maxn=1e6+6;
const int mod=998244353;
const double eps=1e-3;
inline bool read(ll &num)
{char in;bool IsN=false;in=getchar();if(in==EOF) return false;while(in!='-'&&(in<'0'||in>'9')) in=getchar();if(in=='-'){ IsN=true;num=0;}else num=in-'0';while(in=getchar(),in>='0'&&in<='9'){num*=10,num+=in-'0';}if(IsN) num=-num;return true;}
ll n,m,p;
int dfn[maxn],deep[maxn],f[maxn][21];///时间戳 深度 fa数组
vector< pair<int,int> >v[maxn];///存图
vector<int>g[maxn];
int q[maxn];///存lca及询问点
int st[maxn],s = 0;///模拟栈的实现
int cnt = 0,save[maxn];///清空操作
int ldfn = 0;
int vis[maxn],c[maxn];
void dfs(int u,int fa,ll w){deep[u] = deep[fa] + 1;dfn[u] = ++ldfn;f[u][0] = fa;c[u] = min(c[fa]*1ll,w);for(int k=1;k<=20;k++) f[u][k] = f[f[u][k-1]][k-1];for(auto x:v[u]){if(x.first == fa) continue;dfs(x.first,u,x.second);}
}
int LCA(int u,int v){///求lcaif(deep[u] < deep[v]) swap(u,v);for(int k=20;k>=0;k--) if(deep[v]<=deep[f[u][k]]) u = f[u][k];if(u == v) return u;for(int k=20;k>=0;k--){if(f[u][k]!=f[v][k]){u = f[u][k];v = f[v][k];}}return f[u][0];
}
void Insert(int x){///虚树中插入节点xif(s == 1) {st[++s] = x;return ;}int lca = LCA(st[s],x);if(lca == st[s]) {st[++s] = x;return ;}while(s>1&&dfn[lca]<=dfn[st[s-1]]){g[st[s-1]].push_back(st[s]);s--;}if(lca != st[s]){g[lca].push_back(st[s]);st[s] = lca;}st[++s] = x;
}
bool cmp(int a,int b){return dfn[a] < dfn[b];
}
ll Get_Result(int u,int fa){ll temp = 0;for(int e:g[u]) temp += Get_Result(e,u);g[u].clear();if(vis[u]) return c[u];return u==1?temp:min(c[u]*1ll,temp);
}
int main(){read(n);for(int i=1;i<=n-1;i++){int x,y,w;scanf("%d%d%d",&x,&y,&w);v[x].push_back({y,w});v[y].push_back({x,w});}c[1] = 1e9+7;dfs(1,1,1e9+7);read(m);for(int i=1;i<=m;i++){read(p);s = cnt = 0;for(int k=1;k<=p;k++){scanf("%d",&q[k]);save[++cnt] = q[k];vis[q[k]] = 1;}st[++s] = 1;sort(q+1,q+1+p,cmp);for(int k=1;k<=p;k++) Insert(q[k]);while(s>1) g[st[s-1]].push_back(st[s]),s--;printf("%lld\n",Get_Result(1,1));for(int k=1;k<=p;k++) vis[q[k]] = 0;g[1].clear();}return 0;
}
/**
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
4 5 7 8 3
**/

洛谷P2495 [SDOI2011]消耗战 | 一个典型的可以搞懂虚树的例题相关推荐

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

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

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

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

  3. 洛谷 P2495 [SDOI2011]消耗战 题解

    题目链接 题目描述: 给你一个有边权的树,若干次询问,每次询问包含一个不含点111的kkk个点的点集,求点111与这些点都不连通的最小代价(删除一些边,代价是他们的权值和). 解题思路: 首先考虑如果 ...

  4. 洛谷 P2495 [SDOI2011]消耗战

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

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

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

  6. P2495 [SDOI2011]消耗战

    P2495 [SDOI2011]消耗战 虚树+dpdpdp. 每次在整个图上跑dpdpdp时间肯定不够,所以考虑只对查询点和其lcalcalca进行建图. 此题的dpdpdp转移有两种方式:一种是直接 ...

  7. P2495 [SDOI2011]消耗战(树形dp+虚树)

    P2495 [SDOI2011]消耗战 树形dp 状态表示:fuf_ufu​表示以uuu为根的子树中,uuu节点与子树中的关键的"隔开"所需要的最小代价 状态转移: 考虑uuu的一 ...

  8. java线程间通信:一个小Demo完全搞懂

    版权声明:本文出自汪磊的博客,转载请务必注明出处. Java线程系列文章只是自己知识的总结梳理,都是最基础的玩意,已经掌握熟练的可以绕过. 一.从一个小Demo说起 上篇我们聊到了Java多线程的同步 ...

  9. 虚树——P2495 [SDOI2011]消耗战

    好久没有学习新的知识了. https://www.luogu.com.cn/problem/P2495 今天我学习了一下虚树. 虚树就是一个不存在的树.用来简化问题.它只包括一些关键点和他的LCA 我 ...

最新文章

  1. 20172304 《程序设计与数据结构》第九周学习总结
  2. IIS6、7添加反向代理的步骤
  3. Java开发,月薪30k需要掌握哪些主流技术?
  4. BZOJ 2007: [Noi2010]海拔
  5. yii2组件之下拉框带搜索功能(yii-select2)
  6. 直播开发项目发展下半场,转战AI直播开启全新模式
  7. android 广播 7.0变化,安卓7.0到底带来了那些变化?
  8. activemq安全设置 设置admin的用户名和密码
  9. 通过扩展方法,将C#的DateTime(日期)转换成人性化的显示
  10. css transtion不生效_CSS中transition属性不起作用的原因及解决方法
  11. 复习:线性表——顺序表
  12. ASP.NET版在线客服系统源码 带服务端
  13. python软件中文翻译_python 写一个桌面版的翻译软件
  14. LeetCode:每日一题(2020.4.9)
  15. 本科毕设论文查重方法(重点介绍笔杆网站)
  16. 权重的计算方法,主要有两种:1.线性加权法; 2.层次分析法
  17. js轮播图片小圆点变化_原生js实现轮播图的示例代码
  18. 赋能智慧交通的5G关键技术
  19. 微信开发者工具最新版本无法上传腾讯云代码(找不到腾讯云图标)
  20. 细则从哈利·波特与来自您好麻雀船长

热门文章

  1. [强化学习代码笔记]Python复习
  2. Spring学习第6篇: 基于注解使用IOC
  3. 腾云忆想技术文|CREDIS在TMF平台中的落地实践
  4. TMF大数据分析指南 Unleashing Business Value in Big Data(一)
  5. 解决傻妞掉线、装死等系列问题,screen守护永不掉线
  6. linux文件加密代码,OpenSSL 加密文件的完整实现代码
  7. 从普通程序员到身价过百亿:追求长期价值的耐心,决定了你能走多远 原
  8. 如何给孩子选绘本:绘本筛选实践分享
  9. 页面载入-(dom、css、图片 等资源 加载完成) 执行
  10. jmeter阶梯式压测方法Stepping Thread Group