bzoj 2286 SDOI2011 消耗战 虚树dp
2017-02-12 18:28
429 查看
题意就不说了。
分析:
虚树(部分参考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),有以下几种情况。
code:
分析:
这题其实就是让你切代价最小的边使得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 ,pos ,logn,dep ,cnt=0,fa [20],tim,tot=0,head[2*N],head1[2*N]; int b ,q ; ll val ,f ; 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; }
相关文章推荐
- bzoj 2286: [Sdoi2011消耗战 (虚树+树形DP)
- 【bzoj2286】【sdoi2011】【消耗战】【虚树+dp】
- [SDOI2011][bzoj2286] 消耗战 [虚树+dp]
- 【bzoj2286】[Sdoi2011消耗战 虚树+dp
- BZOJ2286 [Sdoi2011]消耗战 (虚树 + 树形DP)
- BZOJ 2286 [Sdoi2011]消耗战(虚树+树形DP)
- BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】
- [BZOJ2286][SDOI2011]消耗战(虚树DP)
- BZOJ2286 [Sdoi2011]消耗战 【虚树 + 树形Dp】
- BZOJ 2286: [Sdoi2011消耗战 [DP 虚树]
- 【虚树+树形DP】BZOJ2286(Sdoi2011)[消耗战]题解
- [bzoj2286][Sdoi2011]消耗战(虚树上的DP)
- bzoj 2286 [Sdoi2011]消耗战(虚树+树上DP)
- 【BZOJ】2286: [Sdoi2011]消耗战 虚树+DP
- [BZOJ2286][SDOI2011]消耗战(虚树+树形DP)
- 【 bzoj 2286 】 : [Sdoi2011]消耗战 - 树形DP
- bzoj 2286: [Sdoi2011消耗战 虚树+树形dp
- [虚树+树形DP]BZOJ 2286—— [Sdoi2011]消耗战
- BZOJ 2286: [Sdoi2011]消耗战 虚树 树形dp
- 【BZOJ2286】【SDOI2011】消耗战 [虚树][树形DP]