C24 虚树 P2495 [SDOI2011] 消耗战

视频链接:247 虚树 [SDOI2011] 消耗战_哔哩哔哩_bilibili

 

 

Luogu P2495 [SDOI2011] 消耗战

复制代码
// 树上倍增+虚树+树形DP 2.0s
#include <iostream>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;

const int N=250005;
int h[N],tot;
struct edge{ int x,y,w,ne; } e[N<<1];
void add(int x,int y,int w){ //连边
  e[++tot]={x,y,w,h[x]}; h[x]=tot;
}
int dep[N],fa[N][20],siz[N];
int dfn[N],cnt; //dfs序
int s[N],top;   //
int n,m,k,a[N];
LL mn[N];    //mn[x]:从根到x的最小边权

void dfs(int x, int f){ //树上倍增
  dfn[x]=++cnt;
  dep[x]=dep[f]+1; fa[x][0]=f; siz[x]=1; 
  for(int i=1; i<=19; i++) 
    fa[x][i]=fa[fa[x][i-1]][i-1];
    
  for(int i=h[x]; i; i=e[i].ne){
    int y=e[i].y;
    if(y==f) continue;
    mn[y]=min(mn[x],(LL)e[i].w);
    dfs(y,x);
    siz[x]+=siz[y];
  }
}
int lca(int x, int y){ //求lca
  if(dep[x]<dep[y])swap(x, y);
  for(int i=19; ~i; i--)
    if(dep[fa[x][i]]>=dep[y])
      x=fa[x][i];
  if(x==y) return y;
  
  for(int i=19; ~i; i--)
    if(fa[x][i]!=fa[y][i])
      x=fa[x][i], y=fa[y][i];
  return fa[x][0];
}
int cmp(int a,int b){ 
  return dfn[a]<dfn[b];
}
void build(){ //建虚树
  sort(a+1,a+k+1,cmp);
  tot=0; //清空
  s[top=1]=1; //根节点入栈
  s[++top]=a[1];
  for(int i=2; i<=k; i++){ //枚举查询点
    int l=lca(a[i],s[top]);
    if(l==s[top]) continue; //剪枝
    // 对当前链连边,top出栈
    while(top>1 && dep[s[top-1]]>=dep[l]) 
      add(s[top-1],s[top],0), top--;
    // 对lca和top连边,top出栈,lca入栈
    if(l!=s[top]) add(l,s[top],0), s[top]=l;
    // 查询点入栈
    s[++top]=a[i];
  }
  while(top) //对最后一条链连边,top出栈
    add(s[top-1],s[top],0),top--;
}
LL DP(int x){ //树形DP
  if(h[x]==0) return mn[x]; //叶子
  LL sum=0;
  for(int i=h[x]; i; i=e[i].ne)
    sum+=DP(e[i].y);
  h[x]=0; //清空
  return min(sum,mn[x]); 
}
int main(){
  scanf("%d",&n);
  for(int i=1; i<=n-1; i++){
    int x,y,z; scanf("%d%d%d",&x,&y,&z);
    add(x,y,z); add(y,x,z);
  }
  mn[1]=2e18; dfs(1,0);
  memset(h+1,0,n<<2); //清空
  scanf("%d",&m);
  while(m--){
    scanf("%d",&k);
    for(int i=1; i<=k; i++) scanf("%d",&a[i]);
    build();
    printf("%lld\n",DP(1));
  }
}
View Code
复制代码

 

复制代码
// 树上倍增+虚树+树形DP 2.1s
#include <iostream>
#include <cstring>
#include <algorithm>
#define LL long long
using namespace std;

const int N=250005;
int h[N],tot;
struct edge{ int x,y,w,ne; } e[N<<1];
void add(int x,int y,int w){ //连边
  e[++tot]={x,y,w,h[x]}; h[x]=tot;
}
int dep[N],fa[N][20],siz[N];
int dfn[N],cnt; //dfs序
int s[N],top;   //
int n,m,k,a[N],vis[N];
LL mn[N]; //mn[x]:从根到x的最小边权

void dfs(int x, int f){ //树上倍增
  dfn[x]=++cnt;
  dep[x]=dep[f]+1; fa[x][0]=f; siz[x]=1; 
  for(int i=1; i<=19; i++) 
    fa[x][i]=fa[fa[x][i-1]][i-1];
    
  for(int i=h[x]; i; i=e[i].ne){
    int y=e[i].y;
    if(y==f) continue;
    mn[y]=min(mn[x],(LL)e[i].w);
    dfs(y,x);
    siz[x]+=siz[y];
  }
}
int lca(int x, int y){ //求lca
  if(dep[x]<dep[y])swap(x, y);
  for(int i=19; ~i; i--)
    if(dep[fa[x][i]]>=dep[y])
      x=fa[x][i];
  if(x==y) return y;
  
  for(int i=19; ~i; i--)
    if(fa[x][i]!=fa[y][i])
      x=fa[x][i], y=fa[y][i];
  return fa[x][0];
}
int cmp(int a,int b){ 
  return dfn[a]<dfn[b];
}
void build(){ //建虚树
  sort(a+1,a+k+1,cmp);
  tot=0; //清空
  s[top=1]=1; //根节点入栈
  s[++top]=a[1];
  for(int i=2; i<=k; i++){ //枚举查询点
    int l=lca(s[top],a[i]);
    // 对当前链连边,top出栈
    while(top>1 && dep[s[top-1]]>=dep[l]) 
      add(s[top-1],s[top],0), top--;
    // 对lca和top连边,top出栈,lca入栈
    if(l!=s[top]) add(l,s[top],0), s[top]=l;
    // 查询点入栈
    s[++top]=a[i];
  }
  while(top) //对最后一条链连边,top出栈
    add(s[top-1],s[top],0),top--;
}
LL DP(int x){ //树形DP
  if(vis[x]){ //x是查询点
    for(int i=h[x]; i; i=e[i].ne)
      DP(e[i].y), vis[e[i].y]=0;
    h[x]=0; //清空
    return mn[x];
  } 
  else{ //x不是查询点
    LL sum=0;
    for(int i=h[x]; i; i=e[i].ne)
      sum+=DP(e[i].y), vis[e[i].y]=0;
    h[x]=0; //清空
    return min(sum,mn[x]);
  }
}
int main(){
  scanf("%d",&n);
  for(int i=1; i<=n-1; i++){
    int x,y,z; scanf("%d%d%d",&x,&y,&z);
    add(x,y,z); add(y,x,z);
  }
  mn[1]=2e18; dfs(1,0);
  memset(h+1,0,n<<2); //清空
  scanf("%d",&m);
  while(m--){
    scanf("%d",&k);
    for(int i=1; i<=k; i++) scanf("%d",&a[i]),vis[a[i]]=1;
    build();
    printf("%lld\n",DP(1));
  }
}
View Code
复制代码

 

复制代码
// 树链剖分+虚树+树形DP 1.5s
#include <iostream>
#include <cstring>
#include <algorithm>
#include <vector>
#define LL long long
using namespace std;

const int N=250005;
int h[N],tot;
struct edge{ int x,y,w,ne; } e[N<<1];
void add(int x,int y,int w){ //连边
  e[++tot]={x,y,w,h[x]}; h[x]=tot;
}
int fa[N],dep[N],siz[N],son[N],tp[N];//重链
int dfn[N],cnt; //dfs序
int s[N],top;   //
int n,m,k,a[N];
LL mn[N];    //mn[x]:从根到x的最小边权

void dfs1(int x, int f){ //树链剖分
  dfn[x]=++cnt; 
  dep[x]=dep[f]+1; siz[x]=1; fa[x]=f;
  for(int i=h[x]; i; i=e[i].ne){
    int y=e[i].y;
    if(y==f) continue;
    mn[y]=min(mn[x],(LL)e[i].w);
    dfs1(y,x);
    siz[x]+=siz[y];
    if(siz[son[x]]<siz[y]) son[x]=y;
  }
}
void dfs2(int x, int t){ //树链剖分
  tp[x]=t; //链头
  if(!son[x]) return;
  dfs2(son[x],t); //搜重儿子
  for(int i=h[x]; i; i=e[i].ne){
    int y=e[i].y;
    if(!tp[y]) dfs2(y,y); //搜轻儿子
  }
}
int lca(int x,int y){ //求lca
  while(tp[x]!=tp[y]){
    if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
    x=fa[tp[x]];
  }
  return dep[x]<dep[y]?x:y;
}
int cmp(int a,int b){ 
  return dfn[a]<dfn[b];
}
void build(){ //建虚树
  sort(a+1,a+k+1,cmp);
  tot=0; //清空
  s[top=1]=1; //根节点入栈
  s[++top]=a[1];
  for(int i=2; i<=k; i++){ //枚举查询点
    int l=lca(a[i],s[top]);
    if(l==s[top]) continue; //剪枝
    // 对当前链连边,top出栈
    while(top>1 && dep[s[top-1]]>=dep[l]) 
      add(s[top-1],s[top],0), top--;
    // 对lca和top连边,top出栈,lca入栈
    if(l!=s[top]) add(l,s[top],0), s[top]=l;
    // 查询点入栈
    s[++top]=a[i];
  }
  while(top) //对最后一条链连边,top出栈
    add(s[top-1],s[top],0),top--;
}
LL DP(int x){ //树形DP
  if(h[x]==0) return mn[x]; //叶子
  LL sum=0;
  for(int i=h[x]; i; i=e[i].ne)
    sum+=DP(e[i].y);
  h[x]=0; //清空
  return min(sum,mn[x]); 
}
int main(){
  scanf("%d",&n);
  for(int i=1; i<=n-1; i++){
    int x,y,z; scanf("%d%d%d",&x,&y,&z);
    add(x,y,z); add(y,x,z);
  }
  mn[1]=2e18; 
  dfs1(1,0); dfs2(1,1);
  memset(h+1,0,n<<2); //清空
  scanf("%d",&m);
  while(m--){
    scanf("%d",&k);
    for(int i=1; i<=k; i++) scanf("%d",&a[i]);
    build();
    printf("%lld\n",DP(1));
  }
}
View Code
复制代码

 

练习:

Luogu P4606 [SDOI2018]战略游戏

Luogu P4103 [HEOI2014] 大工程

Luogu P3233 [HNOI2014] 世界树

Luogu P4242 树上的毒瘤

Luogu P7409 SvT

posted @   董晓  阅读(286)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示