http://www.lydsy.com/JudgeOnline/problem.php?id=2286

题意:n个点的边加权树,m个询问,每次询问给出的k个点与结点1分离的最小代价。(n<=250000, sum{ki}<=500000)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=250005, oo=0x7f7f7f7f;
int dep[N], FF[N], f[N][21], mn[N][21], n, m, a[N], s[N], top, tot, nd[N];
struct Gr {
int ihead[N], cnt;
struct E { int next, to, w; }e[N<<1];
void add(int x, int y, int c=0) {
e[++cnt]=(E){ihead[x], y, c}; ihead[x]=cnt;
e[++cnt]=(E){ihead[y], x, c}; ihead[y]=cnt;
}
void dfs(int x) {
FF[x]=++tot;
for(int i=1; i<=20; ++i) f[x][i]=f[f[x][i-1]][i-1], mn[x][i]=min(mn[x][i-1], mn[f[x][i-1]][i-1]);
for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=f[x][0]) {
f[e[i].to][0]=x; dep[e[i].to]=dep[x]+1; mn[e[i].to][0]=e[i].w;
dfs(e[i].to);
}
}
int LCA(int x, int y) {
if(dep[x]<dep[y]) swap(x, y);
int d=dep[x]-dep[y];
for(int i=20; i>=0; --i) if((d>>i)&1) x=f[x][i];
if(x==y) return x;
for(int i=20; i>=0; --i) if(f[x][i]!=f[y][i]) x=f[x][i], y=f[y][i];
return f[x][0];
}
int dist(int x, int y) {
int ret=oo;
if(dep[x]<dep[y]) swap(x, y);
int d=dep[x]-dep[y];
for(int i=20; i>=0; --i) if((d>>i)&1) ret=min(ret, mn[x][i]), x=f[x][i];
return ret;
}
ll dp(int x, int fa=0) {
ll ret=0;
for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=fa)
ret+=min(nd[e[i].to]?(ll)oo:dp(e[i].to, x), (ll)dist(x, e[i].to));
return ret;
}
void clr(int x, int fa=0) { for(int i=ihead[x]; i; i=e[i].next) if(e[i].to!=fa) clr(e[i].to, x); ihead[x]=0; }
}g, G;
bool cmp(const int &a, const int &b) { return FF[a]<FF[b]; }
int main() {
scanf("%d", &n);
for(int i=0; i<n-1; ++i) { int x, y, c; scanf("%d%d%d", &x, &y, &c); G.add(x, y, c); }
memset(mn, 0x7f, sizeof mn);
G.dfs(1);
int T; scanf("%d", &T);
while(T--) {
scanf("%d", &m);
for(int i=0; i<m; ++i) scanf("%d", &a[i]), nd[a[i]]=1;
sort(a, a+m, cmp);
s[top=1]=1; g.clr(1); g.cnt=0;
for(int i=0; i<m; ++i) {
int x=a[i], lca=G.LCA(x, s[top]);
while(FF[lca]<FF[s[top]]) {
if(FF[lca]>=FF[s[top-1]]) {
g.add(lca, s[top--]);
if(lca!=s[top]) s[++top]=lca;
break;
}
g.add(s[top], s[top-1]); --top;
}
if(s[top]!=x) s[++top]=x;
}
while(--top) g.add(s[top], s[top+1]);
printf("%lld\n", g.dp(1));
for(int i=0; i<m; ++i) nd[a[i]]=0;
}
return 0;
}

  

学习了下虚树= =(为何啥玩意都喜欢加上一个名词?其实虚树 = dfs序 + lca + 单调栈

首先容易想到树dp:$f(x) = \sum_{y是孩子} min(y点必需割?w(x, y):f(y), w(x, y))$

可是复杂度$O(nm)$无法承受...

发现每一次计算很多点是不需要计算的= =比如说两个点之间的链= =用倍增就能得到链的最小值辣...

于是将图重建一个包括x个点以及他们两两之间的lca的树。【可是这样最坏也是n个点都被加入了啊QAQ比如完全二叉树= =...感觉能分分钟卡啊....】(博主纯属sb,泥忘记了有sum ki<=500000的条件么= =

方法就是先求出dfs序然后对于每次询问先对点进行dfs序排序,然后再维护一个dfs序递增的一条链(1点到x点的链)的栈,顺序加入每个点,维护以下性质(包括得到的性质):

1、对任意$i>j$,$dfn(s[i]) > dfn(s[j])$,而排序后可知任意$x$都有$dfn(x)>dfn(s[top])$

2、$f = lca(x, s[top])$在$s[1]~s[top]$中

3、当$f \neq s[top]$时,$f$到$s[top]$之间的点以及$s[top]$的子树都不再对要加入的$lca$有贡献了,因为可以用$f$替代。由前两点性质可证。

所以具体算法就是先按dfs序排序给出的节点$a$,然后依次加入每个点。令$f = lca(s[top], a[now])$,如果$f$在$s[top]$和$s[top-1]$之间,那么$f$和$s[top]$连边,删掉$s[top]$。否则一直删栈顶且连边,直到刚刚满足$f$在$s[top]$和$s[top-1]$之间。最后再加入节点$a[now]$即可。

然后注意最后树dp的时候不要忘记是新图啊!不是原来的father啊!一开始没判re了好多次啊!

(还有写完代码发现和别人的代码相似度为99%是什么鬼啊!

最坏复杂度$O(nm)$,但是数据你懂的= =(所以说虚树属于卡常啊!(博主纯属sb

【BZOJ】2286: [Sdoi2011消耗战的更多相关文章

  1. BZOJ 2286: [Sdoi2011]消耗战

    2286: [Sdoi2011消耗战 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 2082  Solved: 736[Submit][Status] ...

  2. bzoj 2286: [Sdoi2011]消耗战 虚树+树dp

    2286: [Sdoi2011]消耗战 Time Limit: 20 Sec  Memory Limit: 512 MB[Submit][Status][Discuss] Description 在一 ...

  3. bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2286 [题意] 给定一棵树,切断一条树边代价为ci,有m个询问,每次问使得1号点与查询 ...

  4. bzoj 3611: [Heoi2014]大工程 && bzoj 2286: [Sdoi2011消耗战

    放波建虚树的模板. 大概是用一个栈维护根节点到当前关键点的一条链,把其他深度大于lca的都弹出去. 每次做完记得复原. 还有sort的时候一定要加cmp!!! bzoj 3611 #include&l ...

  5. BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp 动态规划 dfs序

    https://www.lydsy.com/JudgeOnline/problem.php?id=2286 wa了两次因为lca犯了zz错误 这道题如果不多次询问的话就是裸dp. 一棵树上多次询问,且 ...

  6. BZOJ.2286.[SDOI2011]消耗战(虚树 树形DP)

    题目链接 BZOJ 洛谷P2495 树形DP,对于每棵子树要么逐个删除其中要删除的边,要么直接断连向父节点的边. 如果当前点需要删除,那么直接断不需要再管子树. 复杂度O(m*n). 对于两个要删除的 ...

  7. BZOJ 2286 [Sdoi2011]消耗战(虚树+树形DP)

    [题目链接] http://www.lydsy.com/JudgeOnline/problem.php?id=2286 [题目大意] 出一棵边权树,每次给出一些关键点,求最小边割集, 使得1点与各个关 ...

  8. bzoj 2286: [Sdoi2011消耗战

    #include<cstdio> #include<iostream> #define M 1000009 #define N 250009 #define ll long l ...

  9. bzoj 2286 [Sdoi2011]消耗战 虚树+dp

    题目大意:多次给出关键点,求切断边使所有关键点与1断开的最小费用 分析:每次造出虚树,dp[i]表示将i和i子树与父亲断开费用 对于父亲x,儿子y ①y为关键点:\(dp[x]\)+=\(dismn( ...

  10. BZOJ 2286: [Sdoi2011消耗战 [DP 虚树]

    传送门 题意: 删除价值和最小的边使得$1$号点与$k$个关键点不连通 一个树形DP...但是询问多次,保证总的关键点数为$O(n)$ 先说一下这个$DP$ $f[i]$表示子树$i$中的关键点与$1 ...

随机推荐

  1. html5 简单音乐播放器

    html5 简单音乐播放器 <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> < ...

  2. js 控制展开折叠 div html dom

    js 控制展开折叠 div    html dom <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" ...

  3. PMP 第一章 引论

    1 项目的特点 独特性 临时性 但创造的成果一般和其特点相反. 2 什么是项目管理? 什么是项目? 项目管理就是将知识 技能 工具与技术应用于项目活动,以满足项目的要求,达到项目的目的. 项目管理通过 ...

  4. 浅学JSON——Json.NET之首次试手

    首次遭遇Json格式,缘由项目中用到Json数据,需要进行解析,为此,将Json数据转为了自己较为熟悉的DataTable格式,以此展示至DataGridView中,验证是否成功. 直接上代码: // ...

  5. Effective C++ 之 Item 2:尽量以 const, enum, inline 替换 #define

    Effective C++ Chapter 1. 让自己习惯C++(Accustoming Yourself to C++) Item 2. 尽量以 const, enum, inline 替换 #d ...

  6. EditText根据焦点弹出软键盘

    //每次启动都清除焦点 myCourse_roomId_input.setText(""); myCourse_roomId_input.clearFocus(); //判断是否获 ...

  7. hibernate常用配置

    核心配置 核心配置有两种方式进行配置 1:属性文件的配置:hibernate.properties 格式:key=value hibernate.connection.driver_class=com ...

  8. WPD:Page Download Time Breakdown选项详解

    WPD:Page Download Time Breakdown选项详解 “页面下载时间细分”图显示每个页面组件下载时间的细分,可以根据它确定在网页下载期间事务响应时间缓慢是由网络错误引起还是由服务器 ...

  9. 70多G的Kindle电子书合集

    70多个G的电子书. 百度网盘分享了几次被屏蔽了, 360网盘每个文件夹最多只能分享2000个文件,所以只能分成多个文件夹分享. 如果大家知道更好的分享方式,欢迎留言相告,多谢: http://yun ...

  10. c#写windows服务(转)

    序言 前段时间做一个数据迁移项目,刚开始用B/S架构做的项目,但B/S要寄存在IIs中,而IIs又不稳定因素,如果重启IIs就要打开页面才能运行项目.有不便之处,就改用Windows服务实现.这篇就总 ...