[SDOI2011]消耗战(虚树)
题意:给定一棵树,割断每一条边都有代价,每次询问会给定一些点,求用最少的代价使所有给定点都和1号节点不连通
暴力\(DP\)
我们先考虑暴力怎么做
设\(dp[u]\)为以\(u\)为根的子树中,割掉所有给定点的最小代价
转移的时候要分两种情况:
1.若u不是给定点,则\(dp[u] = min(u\)到根节点的所有边的最小边长,割掉所有含有给定点的子树)
\(ps:\)上述给定子树不一定与u直接相连
2.若u是给定点,显然他必须与1号点分离,所以\(dp[u]=u\)到根节点的所有边的最小边长
复杂度\(O(nm)\),显然对于\(m>=1\)这种数据是过不了的
下面给出暴力DP代码(吸氧之后有50分):
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define debug printf("Now is Line : %d\n",__LINE__)
#define file(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
//#define int long long
#define inf 1234567890
#define mod 1000000007
il int read()
{
re int x = 0, f = 1; re char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
#define maxn 2333//我也不知道问什么数组开大会从RE 50分->TLE 20分
struct edge
{
int v, w, next;
}e[maxn << 1];
int n, m, head[maxn], cnt, is[maxn], dp[maxn];
il void add(int u, int v, int w)
{
e[++ cnt] = (edge){v, w, head[u]};
head[u] = cnt;
}
il void dfs(int u, int fr)
{
int temp = 0;
for(re int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if(v == fr) continue;
if(is[v]) {temp += e[i].w; continue;}
dp[v] = min(dp[u], e[i].w);
dfs(v, u);
temp += dp[v];
}
dp[u] = min(dp[u], temp);
}
int main()
{
//file(a);
n = read();
for(re int i = 1; i < n; ++ i)
{
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
}
int T = read();
while(T --)
{
memset(is, 0, sizeof(is));
m = read(), dp[1] = inf;
for(re int i = 1; i <= m; ++ i) is[read()] = 1;
dfs(1, 0);
printf("%d\n", dp[1]);
}
return 0;
}
那么剩下五十分我们要怎么得呢?
我们发现\(\sum k[i]\)是非常小的,那我们是不是可以在k上做文(luan)章(gao)呢?
我们发现,有很多点在树上我们是没有用到的,所以我们可以考虑重构一棵新的树,这棵树就叫做————虚树
虚树优化\(DP\)
虚树的思想是只保留有用的点(在这道题目里面显然是标记点和lca),然后重新构建一棵树,从而使节点大大减少,优化复杂度
那么我们要怎么构建虚树呢?
首先对于每一棵树,我们都可以用dfs序表示出来,所以对于每一次询问,我们把所有的标记点按照dfs序排序,然后压进一个栈中,然后连边即可
具体方法:
我们先对所有标记点按照\(dfs\)序排序,并依次入栈
我们考虑入栈操作:
假设我们现在要加入的元素为x,第二个元素为y,栈顶为\(s\),\(l = lca(x, s)\)
因为我们是按\(dfs\)加入,所以\(dfn[s]<dfn[x],dfn[l]<dfn[x]\)
如果l=s,也就是说s是x的祖先,那么s到1号点显然有边需要割掉,所以x对答案并不产生影响,直接忽略
所以x,s肯定在l的两边(\(dfn[l]<dfn[x]\))
然后我们进行分类讨论
\(if(dfn[y] > dfn[l])\) 则说明y在l与x之间,连边\(y -> x\),并将x弹出
\(if(dfn[y] < dfn[l])\) 则说明l在x与y之间,所以连边\(l -> x\),并且令x出栈,l入栈
\(if(dfn[y] = dfn[l])\) 则说明y=l,连边 \(l -> x\)
建树完成以后,每一个节点都是有用点,即满足暴力DP中的含有给定点的子树,所以每一条边都是可以割掉的,直接转移即可
时间复杂度\(O(klogk)\)
代码如下
#include<bits/stdc++.h>
using namespace std;
#define il inline
#define re register
#define debug printf("Now is Line : %d\n",__LINE__)
#define file(a) freopen(#a".in","r",stdin);freopen(#a".out","w",stdout)
#define int long long
#define inf 123456789000000000
#define mod 1000000007
il int read()
{
re int x = 0, f = 1; re char c = getchar();
while(c < '0' || c > '9') { if(c == '-') f = -1; c = getchar();}
while(c >= '0' && c <= '9') x = x * 10 + c - 48, c = getchar();
return x * f;
}
#define maxn 250005
struct edge
{
int v, w, next;
}e[maxn << 1];
int n, m, head[maxn], cnt, is[maxn], mi[maxn], dfn[maxn], col, t;
int size[maxn], fa[maxn], top[maxn], son[maxn], dep[maxn], s[maxn];
vector<int>v[maxn];
il void add(int u, int v, int w)
{
e[++ cnt] = (edge){v, w, head[u]};
head[u] = cnt;
}
il bool cmp(int a, int b){return dfn[a] < dfn[b];}
il void dfs1(int u, int fr)
{
size[u] = 1, fa[u] = fr, dep[u] = dep[fr] + 1;
for(re int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if(v == fr) continue;
mi[v] = min(mi[u], e[i].w);
dfs1(v, u);
if(size[son[u]] < size[v]) son[u] = v;
}
}
il void dfs2(int u, int fr)
{
top[u] = fr, dfn[u] = ++ col;
if(!son[u]) return;
dfs2(son[u], fr);
for(re int i = head[u]; i; i = e[i].next)
{
int v = e[i].v;
if(v != fa[u] && v != son[u]) dfs2(v, v);
}
}
il int lca(int a, int b)
{
while(top[a] != top[b]) dep[top[a]] > dep[top[b]] ? a = fa[top[a]] : b = fa[top[b]];
return dep[a] < dep[b] ? a : b;
}
il void push(int x)
{
if(t == 1) {s[++ t] = x;return;}
int l = lca(x, s[t]);
if(l == s[t]) return;
while(t > 1 && dfn[s[t - 1]] >= dfn[l]) v[s[t - 1]].push_back(s[t]), --t;
if(s[t] != l) v[l].push_back(s[t]), s[t] = l;
s[++ t] = x;
}
il int dp(int u)
{
if(v[u].size() == 0) return mi[u];
int temp = 0;
for(re int i = 0; i < v[u].size(); ++ i) temp += dp(v[u][i]);
v[u].clear();
return min(mi[u], temp);
}
signed main()
{
file(a);
n = read();
for(re int i = 1; i < n; ++ i)
{
int u = read(), v = read(), w = read();
add(u, v, w), add(v, u, w);
}
mi[1] = inf, dfs1(1, 0), dfs2(1, 1);
int T = read();
while(T --)
{
m = read();
for(re int i = 1; i <= m; ++ i) is[i] = read();
sort(is + 1, is + m + 1, cmp);
s[t = 1] = 1;
for(re int i = 1; i <= m; ++ i) push(is[i]);
while(t > 0) v[s[t - 1]].push_back(s[t]), --t;
printf("%lld\n", dp(1));
}
return 0;
}
[SDOI2011]消耗战(虚树)的更多相关文章
- bzoj 2286: [Sdoi2011]消耗战 虚树+树dp
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...
- [BZOJ2286][SDOI2011]消耗战(虚树DP)
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4998 Solved: 1867[Submit][Statu ...
- 【BZOJ2286】[Sdoi2011]消耗战 虚树
[BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的 ...
- bzoj 2286 [Sdoi2011]消耗战 虚树+dp
题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...
- 【BZOJ】2286: [Sdoi2011]消耗战 虚树+DP
[题意]给定n个点的带边权树,每次询问给定ki个特殊点,求隔离点1和特殊点的最小代价.n<=250000,Σki<=500000. [算法]虚树+DP [题解]考虑普通树上的dp,设f[x ...
- [SDOI2011]消耗战(虚树+树形动规)
虚树dp 虚树的主要思想: 不遍历没用的的节点以及没用的子树,从而使复杂度降低到\(\sum\limits k\)(k为询问的节点的总数). 所以怎么办: 只把询问节点和其LCA放入询问的数组中. 1 ...
- bzoj2286: [Sdoi2011]消耗战 虚树
在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的岛屿,而且他们已经没有足够多的能源维系战斗,我军胜利在望.已知在其他k个 ...
- bzoj 2286(洛谷 2495) [Sdoi2011]消耗战——虚树
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=2286 https://www.luogu.org/problemnew/show/P2495 ...
- BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp 动态规划 dfs序
https://www.lydsy.com/JudgeOnline/problem.php?id=2286 wa了两次因为lca犯了zz错误 这道题如果不多次询问的话就是裸dp. 一棵树上多次询问,且 ...
- BZOJ.2286.[SDOI2011]消耗战(虚树 树形DP)
题目链接 BZOJ 洛谷P2495 树形DP,对于每棵子树要么逐个删除其中要删除的边,要么直接断连向父节点的边. 如果当前点需要删除,那么直接断不需要再管子树. 复杂度O(m*n). 对于两个要删除的 ...
随机推荐
- Error: Invoke-customs are only supported starting with Android O (--min-api 26)
项目报错: 完美解决: 在App下 gradle.build中Android标签中 添加以下内容: compileOptions { sourceCompatibility JavaVersion.V ...
- HTTP与HTTPS介绍
文章大纲 一.HTTP和HTTPS的基本概念二.HTTP缺点三.HTTPS介绍四.免费HTTPS证书推荐 一.HTTP和HTTPS的基本概念 HTTP:是互联网上应用最为广泛的一种网络协议,是一个 ...
- postman测试方法,出现400错误码
下午毛概课上帮同学debug了个错误: postman测试 ,得到返回 400的状态码错误: 查询博客: https://blog.csdn.net/zhangmengleiblog/article/ ...
- DVWA 黑客攻防演练(六)不安全的验证码 Insecure CAPTCHA
之前在 CSRF 攻击 的那篇文章的最后,我觉得可以用验证码提高攻击的难度. 若有验证码的话,就比较难被攻击者利用 XSS 漏洞进行的 CSRF 攻击了,因为要识别验证码起码要调用api,跨域会被浏览 ...
- Angualr学习笔记
0.安装即环境初始化 下载node至windows,点击安装,所有环境变量直接OK: linux下载tar后,解压,在/etc/profile的path路径下增加node执行路径: export PA ...
- 执行C#动态代码
执行C#动态代码 using System; using System.Data; using System.Configuration; using System.Text; using Syste ...
- VS2015 IIS Express Web服务器无法启动解决办法
1.运行和调试vs2015项目 提示无法运行项目,打开vs2013项目发现可以正常运行,所以推测试vs2015项目配置有问题. 2.找到项目启动项中 .csproj文件,定位到<WebProje ...
- 把exe注册为windows服务
1.需要工具 Instsrv.exe(可以给系统安装和删除服务) Srvany.exe(可以让程序以服务的方式运行) 2.运行cmd,输入注册服务命令 "instsrv.exe完整路径&qu ...
- 一个小错误:error LNK2019: 无法解析的外部符号 "public: __thiscall Turtle::~Turtle(void)" (??1Turtle@@QAE@XZ),该符号在函数 _main 中被引用
昨天在撸代码的时候遇到了一个十分蛋疼的错误 : 错误: 1>3.obj : error LNK2019: 无法解析的外部符号 "public: __thiscall Turtle::~ ...
- Windows Service 学习系列(二):C# windows服务:安装、卸载、启动和停止Windows Service几种方式
一.通过InstallUtil.exe安装.卸载.启动.停止Windows Service 方法一 1.以管理员身份运行cmd 2.安装windows服务 切换cd C:\Windows\Micros ...