【题解】SDOI2011消耗战
第一次写虚树,感觉好厉害呀~首先,这道题目的树形dp是非常显然的,要控制一个点&其子树所有点,要么在子树内部割边,要么直接切点该点与父亲的连边。所以dp[u]表示控制u点所需的最小代价。只是,注意到这样dp的复杂度是O(nm)的,十分不可接受,妥妥的TLE。不过,题目给出的条件中还有一条:Σki<=500000;说明虽然总共的点很多,但实际上每一次对答案可能有影响的点很少。
再一次想到之前dp的时候就应该发现的性质:一条链上,只需要在意链首的点(控制了链首也就控制了整棵子树),这样我们就可以想到:要是这一棵树很小,能够把对答案没有贡献的点尽量都去掉就可以以很小的复杂度完成每一轮询问的dp了。
怎样构建一棵虚树呢?首先,将整颗树dfs一遍,保存每一个点的dfs序号(记为dfn[i])。对于一次询问中的点:a[i]而言,将其按照dfs序从小到大排序,之后两两求出lca,排除那些在同一条链上的点(只保留链首)。之后,我们将1号点放入栈中。这个栈是一个单调栈,保证在任何时候栈中的元素都是一条链,且栈顶元素深度最大。我们记栈顶元素与当前点(之前保留下来的点)的lca为lca,之后的操作就十分显然了,我们要不断的将栈顶元素退栈(在退栈的同时连边构造虚树),直到退回lca与当前元素的那一条链上。注意如果lca不是最后的栈顶元素,lca也要进栈(在虚树上必须保留的一个点,记录了分叉的情况)。最后不要忘记将剩下的元素也用边连起来。
在虚树上面跑dp,一共也没几个点,自然就跑得很快啦。
#include <bits/stdc++.h>
using namespace std;
#define INF 99999999999LL
#define maxn 280000
#define ll long long
int timer, cnp = , n, m, head[maxn], dfn[maxn], dep[maxn], gra[maxn][];
int a[maxn], s[maxn], top, tot;
ll val[maxn], dp[maxn];
struct edge
{
int to, last;
ll co;
}E[maxn * ]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} bool cmp(int a, int b)
{
return dfn[a] < dfn[b];
} void add(int x, int y, int co = )
{
if(x == y) return;
E[cnp].to = y, E[cnp].co = co;
E[cnp].last = head[x], head[x] = cnp ++;
} int LCA(int x, int y)
{
if(dep[x] < dep[y]) swap(x, y);
for(int i = ; ~i; i --)
if(dep[gra[x][i]] >= dep[y]) x = gra[x][i];
for(int i = ; ~i; i --)
if(gra[x][i] != gra[y][i]) x = gra[x][i], y = gra[y][i];
return (x == y) ? x : gra[x][];
} void dfs(int u, int fa)
{
gra[u][] = fa, dfn[u] = ++ timer, dep[u] = dep[fa] + ;
for(int i = ; i <= ; i ++) gra[u][i] = gra[gra[u][i - ]][i - ];
for(int i = head[u]; i; i = E[i].last)
{
int v = E[i].to;
if(v == fa) continue;
val[v] = min(E[i].co, val[u]);
dfs(v, u);
}
} void DP(int u, int fa)
{
ll c = ; dp[u] = val[u];
for(int i = head[u]; i; i = E[i].last)
{
int v = E[i].to;
if(v == fa) continue;
DP(v, u);
c += dp[v];
}
head[u] = ;
if(c && c < val[u]) dp[u] = c;
} void solve()
{
int k = read();
for(int i = ; i <= k; i ++) a[i] = read();
sort(a + , a + + k, cmp);
cnp = , s[] = ; top = , tot = ;
for(int i = ; i <= k; i ++) if(LCA(a[i], a[tot]) != a[tot]) a[++ tot] = a[i];
for(int i = ; i <= tot; i ++)
{
int lca = LCA(s[top], a[i]);
while()
{
if(dep[lca] >= dep[s[top - ]])
{
add(lca, s[top]);
top --;
if(lca != s[top]) s[++ top] = lca;
break;
}
add(s[top - ], s[top]), top --;
}
s[++ top] = a[i];
}
while(top > ) add(s[top - ], s[top]), top --;
DP(, );
printf("%lld\n", dp[]);
} int main()
{
n = read();
val[] = INF;
for(int i = ; i <= n - ; i ++)
{
int x = read(), y = read(), z = read();
add(x, y, z), add(y, x, z);
}
dfs(, );
memset(head, , sizeof(head));
m = read();
for(int i = ; i <= m; i ++) solve();
return ;
}
【题解】SDOI2011消耗战的更多相关文章
- 【LG2495】[SDOI2011]消耗战
[LG2495][SDOI2011]消耗战 题面 洛谷 题解 参考博客 题意 给你\(n\)个点的一棵树 \(m\)个询问,每个询问给出\(k\)个点 求将这\(k\)个点与\(1\)号点断掉的最小代 ...
- 【BZOJ2286】[Sdoi2011]消耗战 虚树
[BZOJ2286][Sdoi2011]消耗战 Description 在一场战争中,战场由n个岛屿和n-1个桥梁组成,保证每两个岛屿间有且仅有一条路径可达.现在,我军已经侦查到敌军的总部在编号为1的 ...
- BZOJ2286 [Sdoi2011]消耗战 和 BZOJ3611 [Heoi2014]大工程
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 6371 Solved: 2496[Submit][Statu ...
- 洛谷P2495 [SDOI2011]消耗战(虚树dp)
P2495 [SDOI2011]消耗战 题目链接 题解: 虚树\(dp\)入门题吧.虚树的核心思想其实就是每次只保留关键点,因为关键点的dfs序的相对大小顺序和原来的树中结点dfs序的相对大小顺序都是 ...
- BZOJ 2286: [Sdoi2011]消耗战
2286: [Sdoi2011消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 2082 Solved: 736[Submit][Status] ...
- bzoj 2286: [Sdoi2011]消耗战 虚树+树dp
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...
- bzoj千题计划254:bzoj2286: [Sdoi2011]消耗战
http://www.lydsy.com/JudgeOnline/problem.php?id=2286 虚树上树形DP #include<cmath> #include<cstdi ...
- AC日记——[SDOI2011]消耗战 洛谷 P2495
[SDOI2011]消耗战 思路: 建虚树走树形dp: 代码: #include <bits/stdc++.h> using namespace std; #define INF 1e17 ...
- [BZOJ2286][SDOI2011]消耗战(虚树DP)
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MBSubmit: 4998 Solved: 1867[Submit][Statu ...
- BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】
2286: [Sdoi2011]消耗战 Time Limit: 20 Sec Memory Limit: 512 MB Submit: 4261 Solved: 1552 [Submit][Sta ...
随机推荐
- nodejs--http
http模块主要用到四个方法: 1.Server类 const http = require('http'); let server = new Server(); server.on('reques ...
- 系统编程.py(多进程与多线程干货)
1.并发与并行* 多个任务轮换在CPU上跑叫并发* 多个任务在多个CPU上跑,没有交替执行的* 状态叫并行.通常情况下都是并发,即使是多核.* 而控制进程先执行谁后执行谁通过操作系统的调度算法.目前已 ...
- SVN配置自启动-1053错误
主要内容:解决启动“配置的svn自启动服务”报1053错误 1. 环境: 系统: wind10 svn服务端版本: VisualSVN-Server-3.8.0-x64 2. 配置自启动 以管理员身份 ...
- 【Thrift一】Thrift安装部署
Thrift安装部署 Thrift安装部署 下载源码包 安装g++ 解压Thrift安装包 安装boost开发工具 测试(python版) 下载源码包 wget http://apache.fayea ...
- 008---re正则模块
re正则模块 字符串的匹配规则 匹配模式 re.match() re.search() re.findall() re.split() re.sub() 元字符 print('------------ ...
- Python3爬虫(十三) 爬取动态页之Selenium
Infi-chu: http://www.cnblogs.com/Infi-chu/ Python提供了很多模拟浏览器运行的库,比如:Selenium.Splash等 1.常用的引用 from sel ...
- grunt in webstorm
1.install grunt sudo npm install -g grunt-cli npm install grunt --save-dev
- 【转】Python 数据库连接池
python编程中可以使用pymysql进行数据库连接及增删改查操作,但每次连接mysql请求时,都是独立的去请求访问,比较浪费资源,而且访问数量达到一定数量时,对mysql的性能会产生较大的影响.因 ...
- [转] Bash脚本:怎样一行行地读文件(最好和最坏的方法)
用bash脚本读文件的方法有很多.请看第一部分,我使用了while循环及其后的管道命令(|)(cat $FILE | while read line; do … ),并在循环当中递增 i 的值,最后, ...
- 小白学习mysql 之 innodb locks
Innodb 锁类型: Shared and Exclusive Locks Intention Locks Record Locks Gap Locks Next-Key Locks Insert ...