bzoj 2286 SDOI2011 消耗战 虚树dp
来源:互联网 发布:设置数据有效性 编辑:程序博客网 时间:2024/05/17 04:00
题意就不说了。
分析:
这题其实就是让你切代价最小的边使得k个点与根不联通,可以设一个和暴力的DP。设val[i]表示要让i和根断开的代价。很显然有f[i]=min(Σf[son],val[i])val[i]=max(len[i到fa[i]],val[fa[i]]);时间复杂度是O(N),加上询问以后就是O(NM),妥妥的超时。。一开始我还想着是不是要加上数据结构什么的,结果发现好像没有数据结构可做。。。想不出来了,膜一波题解把。好强啊,什么是虚树啊,没见过啊quq。
虚树(部分参考http://blog.csdn.net/lych_cys)
什么是虚树?虚树就是把图里对于答案没有贡献的点删掉。只保留对答案有影响的点,按照原来图的父子关系连边做。
那么具体在这道题目中怎么运用呢。
在每次给出的k个询问点中,设其中两个是x,y。那么设z=lca(x,y)。假如x到z的路径上没有任何点,是其他给出的,任意两个询问点的lca。那么这条路径对于答案很明显没有影响,我们就可以直接把路径压缩为x到z。因为给出了k个点,最多只会有k-1个不同的lca,所以合并时间复杂度是线性的,产生的虚树大小也是一样,O(k).
具体怎么实现呢。
先把给出的点权按照dfs序排序,然后开一个栈,设栈顶是y,每次加入新元素x,假如
lca(x,y)!=y就可以把x加进来,其实也就是把虚树上的一条链加进来。但是还不能直接连边,因为这条链可能还有其他点对的lca。
考虑现在新加进来一个点x,以及栈顶的点p,t=lca(p,x),有以下几种情况。
1.t=p,那么把x加入栈就好了; 2.t!=p,那么显然t是p的祖先,那么显然栈中t->p的路径上面的点都可以加入虚树了,因为不可能会有新的lca插入到t->p的路径中来。 考虑2的具体实现,只要去除栈中的第二个元素,不断比较是否是t的祖先即可。
code:
#include<cstdio>#include<cmath>#include<cstring>#include<algorithm>#include<iostream>#define fo(i,a,b) for(int i=a;i<=b;i++)#define fd(i,a,b) for(int i=a;i>=b;i--)using namespace std;int n,m;typedef long long ll;const int N=250005;struct node{ int next,go,len;}e1[2*N],e2[2*N];int a[N],pos[N],logn,dep[N],cnt=0,fa[N][20],tim,tot=0,head[2*N],head1[2*N];int b[N],q[N];ll val[N],f[N];inline void add(int x,int y,int z){ e1[++cnt].go=y; e1[cnt].next=head[x]; e1[cnt].len=z; head[x]=cnt;}bool cmp(int a,int b){ return pos[a]<pos[b];}inline void add1(int x,int y){ if (x==y)return; e2[++cnt].go=y; e2[cnt].next=head1[x]; head1[x]=cnt;}inline void dfs(int x){ pos[x]=++tim; for (int i=1;i<=logn;i++) fa[x][i]=fa[fa[x][i-1]][i-1]; //fa[x][i]=fa[fa[x][i-1]][i-1]; dep[x]=dep[fa[x][0]]+1; int i=head[x]; while (i) { int v=e1[i].go; if (v!=fa[x][0]) { val[v]=min(val[x],(ll)e1[i].len); fa[v][0]=x; dfs(v); } i=e1[i].next; }}inline int lca(int x,int y){ if(dep[x]<dep[y])swap(x,y); fd(i,logn,0) if (dep[fa[x][i]]>=dep[y])x=fa[x][i]; if (x==y)return x; fd(i,logn,0) if (fa[x][i]!=fa[y][i]) { x=fa[x][i]; y=fa[y][i]; } return fa[x][0];}inline void dp(int x){ f[x]=val[x]; ll s=0; int i=head1[x]; while (i) { int v=e2[i].go; dp(v); s+=f[v]; i=e2[i].next; } if (s<f[x]&&s)f[x]=s; head1[x]=0;}inline void solve(){ int k; scanf("%d",&k); cnt=0; fo(i,1,k)scanf("%d",&a[i]); sort(a+1,a+k+1,cmp); int tot=1; b[tot]=a[1]; fo(i,2,k) if (lca(a[i],b[tot])!=b[tot])b[++tot]=a[i]; int top=1; q[top]=1; fo(i,1,tot) { int x=b[i],f=lca(x,q[top]); while (1) { if (dep[f]>=dep[q[top-1]]) { add1(f,q[top]);top--; break; } add1(q[top-1],q[top]);top--; } if (q[top]!=f)q[++top]=f; if (q[top]!=x)q[++top]=x; } while (top>1)add1(q[top-1],q[top]),top--; dp(1); printf("%lld\n",f[1]);}int main(){ scanf("%d",&n); logn=log(n)/log(2); fo(i,1,n-1) { int x,y,z; scanf("%d%d%d",&x,&y,&z); add(x,y,z); add(y,x,z); } val[1]=1e17; dfs(1); scanf("%d",&m); fo(i,1,m)solve(); return 0;}
0 0
- 【BZOJ】2286: [Sdoi2011消耗战【虚树DP】
- bzoj 2286: [Sdoi2011消耗战 虚树+树形dp
- bzoj 2286 SDOI2011 消耗战 虚树dp
- 【BZOJ 2286】[Sdoi2011消耗战 虚树+dp
- bzoj 2286: [Sdoi2011]消耗战 虚树 DP
- 【BZOJ】2286 [Sdoi2011]消耗战 树形DP+虚树
- bzoj 2286 [Sdoi2011]消耗战 虚树
- BZOJ 2286 [Sdoi2011]消耗战 虚树
- bzoj 2286: [Sdoi2011消耗战 (虚树+树形DP)
- [虚树+树形DP]BZOJ 2286—— [Sdoi2011]消耗战
- 【 bzoj 2286 】 : [Sdoi2011]消耗战 - 树形DP
- BZOJ 2286: [Sdoi2011]消耗战
- bzoj 2286: [Sdoi2011]消耗战
- bzoj 2286: [Sdoi2011]消耗战
- BZOJ 2286: [Sdoi2011]消耗战
- 2286: [Sdoi2011消耗战|树形DP|虚树
- 【bzoj2286】【sdoi2011】【消耗战】【虚树+dp】
- 【bzoj2286】[Sdoi2011消耗战 虚树+dp
- UI自动化测试学习笔记:Espresso (一) BasicSample
- 【CF#612E】Square Root of Permutation 置换群
- C++对c的扩展
- Activity启动过程分析
- 蓝桥杯 新建Microsoft Word文档
- bzoj 2286 SDOI2011 消耗战 虚树dp
- RTThread-笔记0用法拾遗和注意点
- andrid之远程服务
- WC2017滚粗记
- 对象创建、内存布局和访问定位
- AIZU AOJ 2309 Vector Compression 最小树形图(朱—刘算法)
- 2017-02-12学习记录
- Markdown常用语法
- 【算法】上一篇的续:一些计算机操作的二进制总结