您的位置:首页 > 其它

bzoj 2286 SDOI2011 消耗战 虚树dp

2017-02-12 18:28 429 查看
题意就不说了。

分析:

这题其实就是让你切代价最小的边使得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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  虚树 dp bzoj