虚树

作者: 云中翻月 | 来源:发表于2020-11-26 12:00 被阅读0次

适用题目特征

树上有若干关键点(每次询问给出若干关键点),且关键点的总数是与树大小同阶,也就是说实际上一次询问中关键点对于整棵树来说是很稀疏的,所以需要让复杂度由关键点的总数来决定。即把一整颗大树浓缩成一颗小树。

原理

仅包含关键点和他们的LCA的树不会丢失信息。

例题

Luogu P2495
代码如下

/*
Luogu P2495
*/
#define method_1
#ifdef method_1
/*

*/
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<set>
#include<map>
#include<queue>
#include<stack>
#include<vector>
#include<cstring>
#include<cstdlib>
#include<iomanip>
#include<ctime>
#include<string>
#include<bitset>
#define D(x) cout<<#x<<" = "<<x<<"  "
#define E cout<<endl
#define rep(i,s,t) for(int i=(s);i<=(t);i++)
#define rep0(i,s,t) for(int i=(s);i<(t);i++)
#define rrep(i,t,s) for(int i=(t);i>=(s);i--)
using namespace std;
typedef long long ll;
typedef pair<int,int>pii;
const int maxn=250000+5;
const int maxlogn=23;
const int maxm=500000+5;
const ll INF=0x3f3f3f3f3f3f3f3fll;
int n,m;
int t;
int head[maxn],tot=1;
int head2[maxn],tot2;
struct node{
    int from,to;ll w;
}edge[maxm<<1],edge2[maxm<<1];
void add(int from,int to,ll w){
    edge[++tot].from=head[from];
    edge[tot].to=to,edge[tot].w=w,head[from]=tot;
}
void add2(int from,int to){
    edge2[++tot2].from=head2[from];
    edge2[tot2].to=to,head2[from]=tot2;
}
ll mn[maxn];
int d[maxn],f[maxn][maxlogn];
int dfn[maxn],cnt=0;
void dfs(int x){
    dfn[x]=++cnt;
    rep(j,1,t) f[x][j]=f[f[x][j-1]][j-1];
    for(int i=head[x];i;i=edge[i].from){
        int y=edge[i].to;ll w=edge[i].w;
        if(dfn[y]) continue;
        d[y]=d[x]+1,mn[y]=min(mn[x],w),f[y][0]=x;
        dfs(y);
    }
} 
int lca(int x,int y){
    if(d[x]>d[y]) swap(x,y);
    rrep(i,t,0) if(d[f[y][i]]>=d[x]) y=f[y][i];
    if(x==y) return x;
    rrep(i,t,0) if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    return f[x][0];
}
int tr[maxn],q[maxn];
ll dp(int x){ //edge in virtural tree is direct, so don't need to consider edge like <son,fa>
    ll sum=0ll,res;
    for(int i=head2[x];i;i=edge2[i].from){
        int y=edge2[i].to;
        sum+=dp(y);
    }
    if(q[x]) res=mn[x];
    else res=min(mn[x],sum); //cut one edge on [1,x] or cut all child of i from i
    q[x]=0,head2[x]=0;
    return res;
}
bool cmp(int x,int y){
    return dfn[x]<dfn[y];
}
int st[maxn],top;
void build(int num){
    tot2=1;
    sort(tr+1,tr+num+1,cmp);
    st[top=1]=tr[1];
//  rep(i,1,num) D(tr[i]);E;
    rep(i,2,num){
        int now=tr[i],p=lca(now,st[top]);
        while(1){
            if(d[p]>=d[st[top-1]]){
                if(p!=st[top]){
                    add2(p,st[top]);
                    if(p!=st[top-1]) st[top]=p;
                    else top--;
                }
                break;
            }
            else add2(st[top-1],st[top]),top--;
        }
        st[++top]=now;
    }
    while(--top) add2(st[top],st[top+1]);
}
void solve(){
    t=(int)(log(n)/log(2))+1; 
    mn[1]=INF;d[1]=1;
    dfs(1);
//  rep(i,1,n) D(d[i]);E;
    scanf("%d",&m);
    int num;
    while(m--){ 
        scanf("%d",&num);
        rep(i,1,num) scanf("%d",&tr[i]),q[tr[i]]=1;
        build(num);
        ll res=dp(st[1]);
        printf("%lld\n",res);
    }
}
int main() {
//  ios::sync_with_stdio(false);
//  freopen("消耗战.in","r",stdin);
    scanf("%d",&n);
    int from,to;ll w;
    rep(i,1,n-1) scanf("%d%d%lld",&from,&to,&w),add(from,to,w),add(to,from,w);
    solve();
    return 0;
}
#endif
#ifdef method_2
/*

*/

#endif
#ifdef method_3
/*

*/

#endif

相关文章

  • 虚树

    适用题目特征 树上有若干关键点(每次询问给出若干关键点),且关键点的总数是与树大小同阶,也就是说实际上一次询问中关...

  • 水接影树树接影, 虚从实来实无踪。 莫道人从画里来, 丽美只在境界中。

  • 硕果

    树与果 虚与实 奉献与收获 得到与失去 引人触摸而深思……

  • 竹枝·翠屏山

    作者:孤独晴天星痕虚圣 山中树翠路幽深,行人进入难窥寻

  • 菩提无树

    雪落无声 虚极静笃 玉兰有心 菩提无树 ——壬寅惊蛰三侯

  • 暮夜吟(中华新韵)

    风鸣树鸟啼, 云迹水沉鱼。 日落风拾影, 天空夜幕虚。

  • 胡树勇:不要害怕虚

    昨天在本地摄影群聊体育摄影,和晓琴对话: 我:动态是不好拍的!光圈大点、速度适合、虚实结合都难。 晓琴:我用的运动...

  • 花草·虚实

    牵牛花 镜中画 花开花落画中花 画实画虚花不画 何时了却这牵挂 月桂树 虹霓桥 树青树黄桥边树 桥真桥假树旁桥 何...

  • 树叶风

    作者:孤独晴天星痕虚圣 风路过了树, 带走了叶的心, 叶离开了树的怀抱, 树渐渐枯萎, 风给了叶几分钟的美好, 叶...

  • 老样子

    今天只画了树,树比较好画,基本上看不出什么问题了?就是有些是虚的反而画实了,其他还好。

网友评论

      本文标题:虚树

      本文链接:https://www.haomeiwen.com/subject/pggniktx.html