洛谷P2495 [SDOI2011]消耗战(虚树)

博客围绕一道题展开,作者最初代码得90分,重构后才通过。题解先介绍直接用树形dp的做法,记录断开节点的最小值,即根节点到该节点路径上最短边长,还提及节点断开对其子节点的影响,最后提到虚树构建并转载相关博客。

题面

传送门

题解

为啥一直莫名其妙\(90\)分啊……重构了一下代码才\(A\)掉……

先考虑直接\(dp\)怎么做

树形\(dp\)的时候,记一下断开某个节点的最小值,就是从根节点到它的路径上最短的边长,预处理的时候就可以搞出来。然后如果一个节点和根断开了,那么它儿子里所有点都会和根断开

然后是关于虚树的构建……我直接把\(attack\)大佬的博客里说的贴过来好了

1442599-20190318203500944-1066488298.png

//minamoto
#include<bits/stdc++.h>
#define R register
#define ll long long
#define fp(i,a,b) for(R int i=(a),I=(b)+1;i<I;++i)
#define fd(i,a,b) for(R int i=(a),I=(b)-1;i>I;--i)
#define go(u) for(int i=head[u],v=e[i].v;i;i=e[i].nx,v=e[i].v)
using namespace std;
char buf[1<<21],*p1=buf,*p2=buf;
inline char getc(){return p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++;}
int read(){
    R int res,f=1;R char ch;
    while((ch=getc())>'9'||ch<'0')(ch=='-')&&(f=-1);
    for(res=ch-'0';(ch=getc())>='0'&&ch<='9';res=res*10+ch-'0');
    return res*f;
}
char sr[1<<21],z[20];int C=-1,Z=0;
inline void Ot(){fwrite(sr,1,C+1,stdout),C=-1;}
void print(R ll x){
    if(C>1<<20)Ot();if(x<0)sr[++C]='-',x=-x;
    while(z[++Z]=x%10+48,x/=10);
    while(sr[++C]=z[Z],--Z);sr[++C]='\n';
}
const int N=5e5+5;
struct eg{int v,nx,w;}e[N<<1];int head[N],tot;
inline void add(R int u,R int v,R int w){e[++tot]={v,head[u],w},head[u]=tot;}
vector<int>to[N];
inline void add_edge(R int u,R int v){to[u].push_back(v);}
int dfn[N],top[N],fa[N],dep[N],sz[N],son[N],q[N],a[N];ll mn[N];
int n,m,cnt,t;
void dfs1(int u){
    sz[u]=1,dep[u]=dep[fa[u]]+1;
    go(u)if(v!=fa[u]){
        fa[v]=u,mn[v]=min(mn[u],1ll*e[i].w),dfs1(v),sz[u]+=sz[v];
        sz[v]>sz[son[u]]?son[u]=v:0;
    }
}
void dfs2(int u,int t){
    top[u]=t,dfn[u]=++cnt;
    if(!son[u])return;
    dfs2(son[u],t);
    go(u)if(!top[v])dfs2(v,v);
}
int LCA(int u,int v){
    while(top[u]!=top[v]){
        dep[top[u]]<dep[top[v]]?(swap(u,v),0):0;
        u=fa[top[u]];
    }return dep[u]<dep[v]?u:v;
}
void ins(int u){
    int lca=(LCA(u,q[t]));
    if(lca==q[t])return;
    while(t&&dfn[q[t-1]]>=dfn[lca])add_edge(q[t-1],q[t]),--t;
    lca!=q[t]?(add_edge(lca,q[t]),q[t]=lca):0;
    q[++t]=u;
}
ll dp(int u){
    if(to[u].empty())return mn[u];
    ll res=0;
    fp(i,0,to[u].size()-1)res+=dp(to[u][i]);
    vector<int>().swap(to[u]);
    return min(1ll*mn[u],res);
}
inline bool cmp(const int &x,const int &y){return dfn[x]<dfn[y];}
int main(){
//  freopen("testdata.in","r",stdin);
    n=read();
    mn[1]=(1ll<<60);
    for(R int i=1,u,v,w;i<n;++i)u=read(),v=read(),w=read(),add(u,v,w),add(v,u,w);
    dfs1(1),dfs2(1,1);
    m=read();
    while(m--){
        int k=read();
        fp(i,1,k)a[i]=read();
        sort(a+1,a+1+k,cmp);
        q[0]=1,q[t=1]=a[1];
        fp(i,2,k)ins(a[i]);
        while(t)add_edge(q[t-1],q[t]),--t;
        print(dp(1));
    }
    return Ot(),0;
}

转载于:https://www.cnblogs.com/bztMinamoto/p/10554721.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值