• [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
    ",f[1]);
            for(int i=1;i<=dn;i++) head[b[i]]=f[b[i]]=mark[b[i]]=0;
        }
        return 0;
    }
     
  • 相关阅读:
    数组中出现次数超过一半的数字
    Trie字典树算法
    字符串匹配算法 之 基于DFA(确定性有限自动机)
    实现栈最小元素的min函数
    有关有环链表的问题
    浅谈C中的malloc和free
    undefined reference to 'pthread_create'问题解决
    用两个栈实现队列
    resf规范
    单例模式
  • 原文地址:https://www.cnblogs.com/FallDream/p/bzoj2286.html
Copyright © 2020-2023  润新知