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
原创粉丝点击