其他分享
首页 > 其他分享> > [SDOI2011]消耗战

[SDOI2011]消耗战

作者:互联网

O(n^2)的dp很显然

以1为根

f[x]表示把以为根的子树都砍断的最小代价

f[x]=∑min(f[y],e[i].val)

 

但是对于K=500000的

发现,每次用到的关键点并不多,是所有关键点和dfn序相邻关键点的LCA,

这启示我们用虚树!

虚树的边权就是路径上链的最小值

总点数是2*K的

 

至于虚树怎么建?

考虑树欧拉序

虚树的欧拉序的相对顺序显然不变

把所有的点的进栈出栈的两个dfn序都放在数组里排序

然后用栈模拟建出树即可

(当然,由于dfs本身就是栈的操作,所以用栈模拟dfs就不用建树了)

O(K*logN)

代码:

// luogu-judger-enable-o2
#include<bits/stdc++.h>
#define il inline
#define reg register int
#define numb (ch^'0')
using namespace std;
typedef long long ll;
il void rd(int &x){
    char ch;bool fl=false;
    while(!isdigit(ch=getchar()))(ch=='-')&&(fl=true);
    for(x=numb;isdigit(ch=getchar());x=x*10+numb);
    (fl==true)&&(x=-x);
}
namespace Miracle{
const int N=250000+5;
const int inf=0x3f3f3f3f;
int n,m;
struct node{
    int nxt,to;
    int val;
}e[2*N],b[2*N];
int hd[N],cnt;
int pre[N],tot;
void add(int x,int y,int z){
    e[++cnt].nxt=hd[x];
    e[cnt].to=y;
    e[cnt].val=z;
    hd[x]=cnt;
}
int dfn[N],df;
int dfn2[N];
int fdfn[2*N];
int fa[N][20],mi[N][20];
int dep[N];
void dfs(int x,int d){
    dep[x]=d;
    dfn[x]=++df;
    fdfn[df]=x;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fa[x][0]) continue;
        fa[y][0]=x;
        mi[y][0]=e[i].val;
        dfs(y,d+1);
    }
    dfn2[x]=++df;
    fdfn[df]=-x;
}
int lca(int x,int y){
    if(dep[x]<dep[y]) swap(x,y);
    //cout<<" x "<<x<<" : "<<dep[x]<<" "<<fa[x][0]<<endl;
    ///cout<<" y "<<y<<" : "<<dep[y]<<" "<<fa[y][0]<<endl;
    for(reg j=19;j>=0;--j){
        if(dep[fa[x][j]]>=dep[y]){
            x=fa[x][j];
        }
    }
    if(x==y) return x;
    for(reg j=19;j>=0;--j){
        if(fa[x][j]!=fa[y][j]){
            x=fa[x][j],y=fa[y][j];
        }
    }
    return fa[x][0];
}
int fin(int x,int anc){//x!=anc
    int ret=inf;
    for(reg j=19;j>=0;--j){
        if(fa[x][j]&&dep[fa[x][j]]>=dep[anc]){
            ret=min(ret,mi[x][j]);
            x=fa[x][j];
        }
    }
    //cout<<x<<" "<<anc<<" ret "<<ret<<endl;
    return ret;
}
int mem[2*N];
int id[4*N],num;
bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}
int vis[N],has[N];
int sta[4*N],top;
ll f[N];
void sol(int x,int fafa){
    f[x]=0;
    for(reg i=hd[x];i;i=e[i].nxt){
        int y=e[i].to;
        if(y==fafa) continue;
        sol(y,x);
        f[x]+=min(f[y],(ll)e[i].val);
    }
    if(has[x]) f[x]=inf;
}
int main(){
    rd(n);
    int x,y,z;
    for(reg i=1;i<n;++i){
        rd(x);rd(y);rd(z);
        add(x,y,z);add(y,x,z);
    }
    memset(mi,inf,sizeof mi);
    dfs(1,1);
    
    for(reg j=1;j<=19;++j){
        for(reg i=1;i<=n;++i){
            fa[i][j]=fa[fa[i][j-1]][j-1];
            mi[i][j]=min(mi[i][j-1],mi[fa[i][j-1]][j-1]);
        }
    }
    
    int k;
    
    memset(hd,0,sizeof hd);
    cnt=0;
    
    rd(m);
    while(m--){
        rd(k);
        for(reg i=1;i<=k;++i) rd(mem[i]),has[mem[i]]=1;
        sort(mem+1,mem+k+1,cmp);
        top=0;
        tot=k;
        for(reg i=k-1;i>=1;--i){
            if(mem[i]==mem[i+1]) continue;
            int anc=lca(mem[i],mem[i+1]);
        //    cout<<mem[i]<<" and "<<mem[i+1]<<" anc "<<anc<<endl;
            mem[++tot]=anc;
        }
        mem[++tot]=1;
        
        num=0;
        for(reg i=1;i<=tot;++i){
        //    cout<<" i "<<mem[i]<<" "<<dfn[mem[i]]<<" "<<dfn2[mem[i]]<<endl;
            id[++num]=dfn[mem[i]];id[++num]=dfn2[mem[i]];
        }
        sort(id+1,id+num+1);
        num=unique(id+1,id+num+1)-id-1;
        
        top=0;
        cnt=0;
        x=0;
        for(reg i=1;i<=num;++i){
            if(fdfn[id[i]]>0){
                x=fdfn[id[i]];
                if(top) add(sta[top],x,fin(x,sta[top]));
                sta[++top]=x;    
            }else{
                sta[top--]=0;
            }
        }
        sol(1,0);
        
        printf("%lld\n",f[1]);
        
        
        for(reg i=1;i<=tot;++i){
            has[mem[i]]=0;
            hd[mem[i]]=0;
        }
        cnt=0;
    }
    return 0;
}

}
signed main(){
    Miracle::main();
    return 0;
}

/*
   Author: *Miracle*
   Date: 2019/1/31 12:55:30
*/

总结:

思路还是直观自然的

我们发现实际用到的点不是很多。考虑值保留关键点。就用到了虚树

虚树的建立,这里欧拉序起到了很大的作用。

因为有欧拉序是可以还原出来一个树的

没有怎么接触过的

标签:dep,cnt,fa,int,虚树,SDOI2011,消耗战,reg
来源: https://www.cnblogs.com/Miracevin/p/10347241.html