[bzoj2286][Sdoi2011]消耗战

来自FallDream的博客,未经允许,请勿转载,谢谢。

一棵n的点的树,有边权;m次询问,每次给定k个点,要求剪断一些边使得点1无法到达这k个点,求最小代价。n<=250000 sigma(ki)<=500000
 
首先如果只有一个询问的树形dp大家肯定都会吧。
对于一条u->v且边权是w的边,如果v是k个点之一,那么f[u]+=w,否则f[u]+=min(f[v],w)
然后这道题很明显就是要你建出虚树,边权倍增算算呗,然后那么dp就好了啊    复杂度nlogn
然后数据有点坑,写了个假的边权范围,最后一个点会爆int
自己yy的一个虚树建法,感觉好奇怪啊TAT
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#define MN 250000
#define MD 17
#define INF 2000000000
#define ll long long
using namespace std;
inline int read()
{
    int x = 0 , f = 1; char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') f = -1;  ch = getchar();}
    while(ch >= '0' && ch <= '9'){x = x * 10 + ch - '0';ch = getchar();}
    return x * f;
}

int fa[MD+1][MN+5],c[MD+5][MN+5],n,m,head[MN+5],cnt=0,dep[MN+5];
int dn=0,dfn[MN+5],a[MN+5],q[MN+5],top,b[MN+5];
ll f[MN+5];
struct edge{int to,next,w;}e[MN*2+5];
bool mark[MN+5];

inline void ins(int f,int t,int w)
{
    e[++cnt]=(edge){t,head[f],w};head[f]=cnt;
    e[++cnt]=(edge){f,head[t],w};head[t]=cnt;
} 

void init(int x,int f,int last)
{
    fa[0][x]=f;c[0][x]=last;dfn[x]=++dn;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=f)
        {
            dep[e[i].to]=dep[x]+1;
            init(e[i].to,x,e[i].w);
        }
}

int lca(int x,int y)
{
    if(dep[x]<dep[y]) swap(x,y);
    for(int k=dep[x]-dep[y],j=0;k;k>>=1,++j)
        if(k&1) x=fa[j][x];
    if(x==y) return x;
    for(int i=MD;i>=0;i--)
        if(fa[i][x]!=fa[i][y])
            x=fa[i][x],y=fa[i][y];
    return fa[0][x];
}

int getc(int x,int y)
{
    int mx=INF;
    for(int i=MD;i>=0;--i)
        if(dep[fa[i][y]]>=dep[x]) mx=min(mx,c[i][y]),y=fa[i][y];
    return mx;
}

bool cmp(int x,int y){return dfn[x]<dfn[y];}
void build()
{
    a[++m]=1;cnt=top=dn=0;
    sort(a+1,a+m+1,cmp);
    for(int i=1+(a[1]==a[2]);i<=m;i++)
    {
        if(top)
        {
            int x=lca(a[i],q[top]);
            if(x!=q[top])
            {
                while(top>1&&dep[q[top-1]]>=dep[x])
                    ins(q[top-1],q[top],getc(q[top-1],q[top])),--top;
                if(top&&q[top]!=x&&dep[q[top-1]]<dep[x]) ins(x,q[top],getc(x,q[top])),q[top]=x;
            }
        }
        q[++top]=a[i];
    }
    for(;top>1;--top) ins(q[top-1],q[top],getc(q[top-1],q[top]));
}

void dp(int x,int fa)
{
    b[++dn]=x;f[x]=0;
    for(int i=head[x];i;i=e[i].next)
        if(e[i].to!=fa)
        {
            dp(e[i].to,x);
            if(mark[e[i].to]) f[x]+=e[i].w;
            else f[x]+=min((ll)e[i].w,f[e[i].to]); 
        }
}

int main()
{
    n=read();
    for(int i=1;i<n;i++) 
    {
        int u=read(),v=read(),w=read();
        ins(u,v,w);    
    }    
    init(dep[1]=1,0,INF);
    for(int i=0;i<=MD;i++) c[i][0]=INF;
    for(int j=1;j<=MD;j++)
        for(int i=1;i<=n;i++)
            fa[j][i]=fa[j-1][fa[j-1][i]],c[j][i]=min(c[j-1][i],c[j-1][fa[j-1][i]]);
    memset(head,0,sizeof(head));
    for(int T=read();T;--T)
    {
        m=read();
        for(int i=1;i<=m;i++) mark[a[i]=read()]=1;
        build();dp(1,0);
        printf("%lld\n",f[1]);
        for(int i=1;i<=dn;i++) head[b[i]]=f[b[i]]=mark[b[i]]=0;
    }
    return 0;
}

 

 
posted @ 2017-04-19 09:38  FallDream  阅读(205)  评论(0编辑  收藏  举报