虚树总结

Android16 RK3576 Bluetooth source和sink模式切换 摘要:本文介绍了在RK3576平台上实现蓝牙A2DP模式动态切换的修改方案。通过在device配置文件中添加A2DP Sink相关属性,修改Bluetooth模块代码实现:1)在Config.java中增加persist.bluetooth.rka2dpsink属性控制模式切换;2)修改A2dpSinkStreamHandler.java取消音频焦点限制;3)调整btif_a2dp_sink.cc默认授予音频焦点。最终通过设置persist.bluetooth.rka2dpsink属性为true/false 阅读详情
之前的之前

之前的之前,可以做做这题,领略虚树的思想
洛谷P3320 [SDOI2015]寻宝游戏
我在洛谷上的题解

学习之前的例题

在学虚树之前,先来看一道题:
洛谷P2495 [SDOI2011]消耗战
大致题意是这样的,现在有n个点,n-1条带权边的树,有m个询问,每个询问,给出k个点,(其中不包含1号节点),要删除一些边使得1号点与给出的点不连通,求删除边和的最小值。

乍一看,是不是觉得显然是树形dp呢,而且树形dp还是 Θ ( n ) \Theta(n) Θ(n)的,十分优越
但是,不幸的是, 2 ≤ n ≤ 250000 2\le n\le250000 2≤n≤250000的情况下, 1 ≤ m ≤ 500000 1\le m\le500000 1≤m≤500000,如果是 Θ ( n m ) \Theta(nm) Θ(nm)显然会TLE,但是又有一个好消息 Σ k i ≤ 500000 \Sigma k_i\le500000 Σki​≤500000。

那么考虑一下怎么做,可能有些难想,这个时候就该引入虚树啦,此题可以用虚树解决

虚树的概念

虚树,就是在有一棵树的情况下,对于数量较少的点进行询问时所建的一棵新的树,虚树包含询问的点和询问的点的lca(最近公共祖先),上面的点被称为关键点。对于两个关键点A,B,它们的连边上包含着原本树上两点之间那条链上的关键信息(这个信息可以是边权最大值、边权最小值、或者是边权之和,这个取决于实际需要),然后就可以进行树形dp了,这样的复杂度是基于询问的点数的,就可以想象虚树就是把一棵大树浓缩成一棵拥有所有你需要的信息的小树。

建树的方法

那么怎么建树呢,比较常见的做法是维护一个栈,里面存储着一条链,每一次加入一个点进行操作。
具体列举一下步骤吧

预备知识:

用较优的复杂度求lca,以及求两点之间的距离,一般倍增做,或者树链剖分加前缀和都可以,这都是单次 Θ ( l o g n ) \Theta(logn) Θ(logn)的,另外呢,要事先求好原树的dfs序和每个点的深度(这里的深度是指点序的深度,在计算这个深度的时候每条边都是为1来算),后面要用

询问操作:

1.输入每个询问的点,并且按照dfs序为关键字排序
2.将第1个点压到栈当中,开始构建虚树
3.枚举到下一个点u,计算u与栈顶点v的公共祖先lca
4.假设栈中栈顶下方的点为w(若栈中只有1个点就直跳过这一步),若w点的深度大于lca就把v向w连一条边,并且弹掉v,重复此步,否则就到下一步
5.若lca不是当前的v,那么就把lca和v连边,把v弹出,让lca成为栈顶元素(注:这个操作的意思是如果栈顶没有这个lca那么就压入),否则不做任何操作
6.最后把u压入栈中
7.回到3操作枚举下个点,直到枚举完了所有点
8.把栈顶v与栈顶下方的点为w连边,并且把v弹掉,这么做直到栈里只有一个点
9.栈里剩下的点就是虚树的根了
接下来你就可以开始进行dp等操作了

虚树的复杂度

虚树的建树的复杂度是 Θ ( k l o g n ) \Theta(klogn) Θ(klogn)的,树形dp就是 Θ ( k ) \Theta(k) Θ(k)的啦,因为考虑最后虚树上的关键点有询问的点,和lca,然后每个询问的点最多产生1个新的lca,所以复杂度就是对的啦

有关虚树用建树的几点技巧

首先栈里面最后剩下的点就是虚树的根节点,这个很好用
然后对于很多题目建虚树都要建多次,那么 邻接表/vector 的同学都要清空 head/vector ,如果 memset/一个个枚举clear 那肯定会T的,我往往都会在树形dp时用完边后进行清空,就非常高效了

开头的题的题解

对于这一题把1号点作为根,对于每个询问都建一棵虚树,然后进行树形dp,这一题当中可以发现若一个点是另一个点的祖先,那么只需保留深度小的那个点,那么建好的虚树中只有叶子节点是需要被截断的,树形dp可以用f[i][0]表示截断i点的子节点的最小代价,f[i][1]表示不截断的最小代价(当然可以少一维,但是为了方便我就这么定义啦)
下面是代码

#include<cstdio>
#include<cctype>
namespace fast_IO
{
    const int IN_LEN=10000000,OUT_LEN=10000000;
    char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
    inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
    inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
    inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
typedef long long LL;
#define rg register
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(a%b==0)return b;return gcd(b,a%b);}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
    char cu=getchar();x=0;bool fla=0;
    while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
    while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
    if(fla)x=-x; 
}
template <typename T> void printe(const T x)
{
    if(x>=10)printe(x/10);
    putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
    if(x<0)putchar('-'),printe(-x);
    else printe(x);
}
inline void judge()
{
    freopen("1.in","r",stdin);
    freopen("1.out","w",stdout);
}
#include<cstring>
#include<algorithm>
const int maxn=250001,maxm=500001;
int bit[19];
int n,m,s;
int head[maxn],nxt[maxm],tow[maxm],vau[maxm],tmp=1;
int who[maxn][19],dis[maxn][19];
int dep[maxn];
inline void addb(const int u,const int v,const int w)
{
    tmp++;
    nxt[tmp]=head[u];
    head[u]=tmp;
    tow[tmp]=v;
    vau[tmp]=w;
}
int tid[maxn],tim;
inline void dfs(const int u)
{
    tid[u]=++tim;
    for(rg int i=head[u];i;i=nxt[i])
    {
        const int v=tow[i];
        if(who[u][0]!=v)
        {
            who[v][0]=u;
            dis[v][0]=vau[i];
            dep[v]=dep[u]+1;
            dfs(v);
        }
    }
}
inline int lca(int a,int b)
{
    if(dep[a]<dep[b])swap(a,b);
    const int lenth=dep[a]-dep[b];
    for(rg int i=0;bit[i]<=lenth;i++)
        if(lenth&bit[i])
            a=who[a][i];
    if(a==b)return a;
    for(rg int i=18;i>=0;i--)
        if(who[a][i]!=who[b][i])
            a=who[a][i],b=who[b][i];
    return who[a][0];
}
inline int dist(int a,int b)
{
    rg int ans=0x7fffffff;
    if(dep[a]<dep[b])swap(a,b);
    const int lenth=dep[a]-dep[b];
    for(rg int i=0;bit[i]<=lenth;i++)
        if(lenth&bit[i])
            ans=min(ans,dis[a][i]),a=who[a][i];
    if(a==b)return ans;
    for(rg int i=18;i>=0;i--)
        if(who[a][i]!=who[b][i])
            ans=min(ans,min(dis[a][i],dis[b][i])),a=who[a][i],b=who[b][i];
    return min(ans,min(dis[a][0],dis[b][0]));
}
int head_[maxn],nxt_[maxm],tow_[maxm],vau_[maxm],tmp_;
inline void addb_(const int u,const int v,const int w)
{
    tmp_++;
    nxt_[tmp_]=head_[u];
    head_[u]=tmp_;
    tow_[tmp_]=v;
    vau_[tmp_]=w;
}
int k,h[maxn];
bool cmp(const int x,const int y)
{
    return tid[x]<tid[y];
}
int stack[maxn],top;
LL dp[maxn][2];
void dfs(const int u,const int fa)
{
    rg bool sign=1;
    dp[u][0]=dp[u][1]=0;
    for(rg int i=head_[u];i;i=nxt_[i])
    {
        const int v=tow_[i];
        if(v!=fa)
        {
            dfs(v,u);
            dp[u][0]+=min(dp[v][0],dp[v][1]+vau_[i]);
            dp[u][1]+=dp[v][1];
            sign=0;
        }
    }
    head_[u]=0;
    if(sign)dp[u][0]=0x7fffffffffffffff;
    dp[u][1]=min(dp[u][1],dp[u][0]);
}
int main()
{
//	judge();
    memset(dis,0x7f,sizeof(dis));
    bit[0]=1;
    for(rg int i=1;i<=18;i++)bit[i]=bit[i-1]<<1;
    read(n),s=1;
    for(rg int i=1;i<n;i++)
    {
        int u,v,w;read(u),read(v),read(w);
        addb(u,v,w),addb(v,u,w);
    }
    who[s][0]=s,dep[s]=1;
    dfs(s);
    for(rg int j=1;j<=18;j++)
        for(rg int i=1;i<=n;i++)
        	who[i][j]=who[who[i][j-1]][j-1],dis[i][j]=min(dis[i][j-1],dis[who[i][j-1]][j-1]);
    read(m);
    for(rg int i=1;i<=m;i++)
    {
        read(k);
		for(rg int j=1;j<=k;j++)read(h[j]);
        std::sort(h+1,h+k+1,cmp);
        tmp_=0;
        rg int sum=0;
        h[++sum]=h[1];
        for(rg int j=2;j<=k;j++)if(lca(h[sum],h[j])!=h[sum])h[++sum]=h[j];
        k=sum;
        top=0,stack[++top]=1;
        for(rg int j=1;j<=k;j++)
        {
            const int LCA=lca(stack[top],h[j]);
            while(top>1&&dep[stack[top-1]]>dep[LCA])
            {
                const int DIS=dist(stack[top-1],stack[top]);
                addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);
                top--;
            }
            if(dep[stack[top]]>dep[LCA])
            {
                const int DIS=dist(LCA,stack[top]);
                addb_(LCA,stack[top],DIS),addb_(stack[top],LCA,DIS);
                top--;
            }
            if(!top||dep[stack[top]]<dep[LCA])stack[++top]=LCA;
            stack[++top]=h[j];
        }
        while(top>1)
        {
            const int DIS=dist(stack[top-1],stack[top]);
            addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);
            top--;
        }
        dfs(1,0);
        print(dp[1][0]);
        putchar('\n');
    }
    return 0;
}
再来一题

由于前面那题有一些特殊性质,代码可能不能作为虚树的模板,所以呢,现在再引入一道题
洛谷P3233 [HNOI2014]世界树
简要题意就是现在有一棵n个点,n-1条边的树,给出q个询问,每次询问给出mi个重要点,树上的每一个点都属于离其最近的重要点,若有多个点满足条件,那么它属于这其中编号最小的点,对于每个询问中需要输出每个给出的重要点所管辖点的数量。n<=300000,q<=300000,m[1]+m[2]+…+m[q]<=300000
怎么做,很显然还是通过建虚树然后进行树形dp啦,这道题其实有些麻烦,后面的树形dp贼烦(为了便于理解,我dfs了多遍,导致代码量大大增加),对于每条虚树边都可能属于一个点或者被瓜分成两份,对于虚树上的非关键点,首先计算它属于哪个点,然后算出它的出边的树中不属于自己所在重要点的点的数量(除了朝向自己属于的重要点的方向,这个可以通过和重要点的距离来判),然后对于每一个重要点所管辖点的数量是n-不属于自己的点的数量就好了。解释可能不是很清楚,下面贴出代码(update:由于写此份代码的时候太菜,思路不够清晰,所以当时为了方便思考导致树形dp这一部分写的很长,实际树形dp的清真实现可以去参考网上的其它代码):

#include<cstdio>
#include<cctype>
#include<algorithm>
#include<cstring> 
namespace fast_IO
{
    const int IN_LEN=10000000,OUT_LEN=10000000;
    char ibuf[IN_LEN],obuf[OUT_LEN],*ih=ibuf+IN_LEN,*oh=obuf,*lastin=ibuf+IN_LEN,*lastout=obuf+OUT_LEN-1;
    inline char getchar_(){return (ih==lastin)&&(lastin=(ih=ibuf)+fread(ibuf,1,IN_LEN,stdin),ih==lastin)?EOF:*ih++;}
    inline void putchar_(const char x){if(oh==lastout)fwrite(obuf,1,oh-obuf,stdout),oh=obuf;*oh++=x;}
    inline void flush(){fwrite(obuf,1,oh-obuf,stdout);}
}
using namespace fast_IO;
#define getchar() getchar_()
#define putchar(x) putchar_((x))
#define rg register
typedef long long LL;
template <typename T> inline T max(const T a,const T b){return a>b?a:b;}
template <typename T> inline T min(const T a,const T b){return a<b?a:b;}
template <typename T> inline void mind(T&a,const T b){a=a<b?a:b;}
template <typename T> inline void maxd(T&a,const T b){a=a>b?a:b;}
template <typename T> inline T abs(const T a){return a>0?a:-a;}
template <typename T> inline void swap(T&a,T&b){T c=a;a=b;b=c;}
template <typename T> inline T gcd(const T a,const T b){if(!b)return a;return gcd(b,a%b);}
template <typename T> inline T lcm(const T a,const T b){return a/gcd(a,b)*b;}
template <typename T> inline T square(const T x){return x*x;};
template <typename T> inline void read(T&x)
{
    char cu=getchar();x=0;bool fla=0;
    while(!isdigit(cu)){if(cu=='-')fla=1;cu=getchar();}
    while(isdigit(cu))x=x*10+cu-'0',cu=getchar();
    if(fla)x=-x;
}
template <typename T> inline void printe(const T x)
{
    if(x>=10)printe(x/10);
    putchar(x%10+'0');
}
template <typename T> inline void print(const T x)
{
    if(x<0)putchar('-'),printe(-x);
    else printe(x);
}
const int maxn=300001,maxm=600001;
int bit[19];
int n,m,s;
int head[maxn],nxt[maxm],tow[maxm],tmp=1;
int who[maxn][19],dis[maxn][19];
int dep[maxn];
inline void addb(const int u,const int v)
{
    tmp++;
    nxt[tmp]=head[u];
    head[u]=tmp;
    tow[tmp]=v;
}
int tid[maxn],tim,size[maxn];
inline void dfs(const int u)
{
    tid[u]=++tim,size[u]=1;
    for(rg int i=head[u];i;i=nxt[i])
    {
        const int v=tow[i];
        if(who[u][0]!=v)
        {
            who[v][0]=u;
            dis[v][0]=1;
            dep[v]=dep[u]+1;
            dfs(v);
            size[u]+=size[v]; 
        }
    }
}
inline int lca(int a,int b)
{
    if(dep[a]<dep[b])swap(a,b);
    const int lenth=dep[a]-dep[b];
    for(rg int i=0;bit[i]<=lenth;i++)
        if(lenth&bit[i])
            a=who[a][i];
    if(a==b)return a;
    for(rg int i=18;i>=0;i--)
        if(who[a][i]!=who[b][i])
            a=who[a][i],b=who[b][i];
    return who[a][0];
}
inline int dist(int a,int b)
{
    rg int ans=0;
    if(dep[a]<dep[b])swap(a,b);
    const int lenth=dep[a]-dep[b];
    for(rg int i=0;bit[i]<=lenth;i++)
        if(lenth&bit[i])
            ans+=dis[a][i],a=who[a][i];
    if(a==b)return ans;
    for(rg int i=18;i>=0;i--)
        if(who[a][i]!=who[b][i])
            ans+=dis[a][i]+dis[b][i],a=who[a][i],b=who[b][i];
    return ans+dis[a][0]+dis[b][0];
}
int head_[maxn],nxt_[maxm],tow_[maxm],vau_[maxm],tmp_;
inline void addb_(const int u,const int v,const int w)
{
    tmp_++;
    nxt_[tmp_]=head_[u];
    head_[u]=tmp_;
    tow_[tmp_]=v;
    vau_[tmp_]=w;
}
int k,h[maxn],ans[maxn],belo[maxn],Dis[maxn];
int qu[maxn];
bool sign[maxn];
bool cmp(const int x,const int y)
{
    return tid[x]<tid[y];
}
int stack[maxn],top;
inline int check(int u,const int lenth)
{
    for(rg int i=0;bit[i]<=lenth;i++)
        if(lenth&bit[i])
            u=who[u][i];
    return u;
}
void dfs1_0(const int u,const int fa)
{
    if(sign[u])
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs1_0(v,u);
        }
    }
    else
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)
            {
                dfs1_0(v,u);
                if(sign[v])
                {
                    if(vau_[i]<Dis[u]||vau_[i]==Dis[u]&&v<belo[u])
                        belo[u]=v,Dis[u]=vau_[i];
                }
                else
                {
                    if(vau_[i]+Dis[v]<Dis[u]||vau_[i]+Dis[v]==Dis[u]&&belo[v]<belo[u])
                        belo[u]=belo[v],Dis[u]=vau_[i]+Dis[v];
                }
            }
        }
    }
}
void dfs1_1(const int u,const int fa)
{
    if(sign[u])
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs1_1(v,u);
        }
    }
    else
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v==fa)
            {
                if(sign[v])
                {
                    if(vau_[i]<Dis[u]||vau_[i]==Dis[u]&&v<belo[u])
                        belo[u]=v,Dis[u]=vau_[i];
                }
                else
                {
                    if(vau_[i]+Dis[v]<Dis[u]||vau_[i]+Dis[v]==Dis[u]&&belo[v]<belo[u])
                        belo[u]=belo[v],Dis[u]=vau_[i]+Dis[v];
                }
            }
        }
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs1_1(v,u);
        }
    }
}
void dfs2(const int u,const int fa)
{
    if(sign[u])
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs2(v,u);
        }
    }
    else
    {
        ans[u]=0;
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(belo[v]==belo[u]&&Dis[u]>Dis[v]||v==belo[u])continue;
            rg int p;
            if(v!=fa)dfs2(v,u);
            if(belo[v]==belo[u])ans[u]+=ans[v];
            else if(sign[v])
            {
                if(v!=fa)
                {
                    if((Dis[u]+vau_[i])&1)p=check(v,(Dis[u]+vau_[i])/2);
                    else if(v<belo[u])p=check(v,(Dis[u]+vau_[i])/2);
                    else p=check(v,(Dis[u]+vau_[i])/2-1);
                    ans[u]+=size[p];
                }
                else
                {
                    if((Dis[u]+vau_[i])&1)p=check(u,(Dis[u]+vau_[i])/2-Dis[u]);
                    else if(v>belo[u])p=check(u,(Dis[u]+vau_[i])/2-Dis[u]);
                    else p=check(u,(Dis[u]+vau_[i])/2-Dis[u]-1);
                    ans[u]+=n-size[p];
                }
            }
            else
            {
                if(v!=fa)
                {
                    if((Dis[u]+Dis[v]+vau_[i])&1)p=check(v,(Dis[u]+Dis[v]+vau_[i])/2-Dis[v]);
                    else if(belo[v]<belo[u])p=check(v,(Dis[u]+Dis[v]+vau_[i])/2-Dis[v]);
                    else p=check(v,(Dis[u]+Dis[v]+vau_[i])/2-Dis[v]-1);
                    ans[u]+=size[p];
                }
                else
                {
                    if((Dis[u]+Dis[v]+vau_[i])&1)p=check(u,(Dis[u]+Dis[v]+vau_[i])/2-Dis[u]);
                    else if(belo[v]>belo[u])p=check(u,(Dis[u]+Dis[v]+vau_[i])/2-Dis[u]);
                    else p=check(u,(Dis[u]+Dis[v]+vau_[i])/2-Dis[u]-1);
                    ans[u]+=n-size[p];
                }
            }
        }
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(belo[v]==belo[u]&&Dis[u]>Dis[v]||v==belo[u])
                if(v!=fa)
                    dfs2(v,u);
        }
    }
}
void dfs3(const int u,const int fa)
{
    if(sign[u])
    {
        ans[u]=n;
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            rg int p; 
            if(v!=fa)dfs3(v,u);
            if(belo[v]==u)ans[u]-=ans[v];
            else if(sign[v])
            {
                if(v!=fa)
                {
                    if(vau_[i]&1)p=check(v,vau_[i]/2);
                    else if(v<u)p=check(v,vau_[i]/2);
                    else p=check(v,vau_[i]/2-1);
                    ans[u]-=size[p];
                }
                else
                {
                    if(vau_[i]&1)p=check(u,vau_[i]/2);
                    else if(v>u)p=check(u,vau_[i]/2);
                    else p=check(u,vau_[i]/2-1);
                    ans[u]-=n-size[p];
                }
            }
            else
            {
                if(v!=fa)
                {
                    if((Dis[v]+vau_[i])&1)p=check(v,(Dis[v]+vau_[i])/2-Dis[v]);
                    else if(u<belo[v])p=check(v,(Dis[v]+vau_[i])/2-Dis[v]-1);
                    else p=check(v,(Dis[v]+vau_[i])/2-Dis[v]);
                    ans[u]-=size[p];
                }
                else
                {
                    if((Dis[v]+vau_[i])&1)p=check(u,(Dis[v]+vau_[i])/2);
                    else if(u>belo[v])p=check(u,(Dis[v]+vau_[i])/2-1);
                    else p=check(u,(Dis[v]+vau_[i])/2);
                    ans[u]-=n-size[p];
                }
            }
        }
    }
    else
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs3(v,u);
        }
    }
}
void dfs4(const int u,const int fa)
{
    if(sign[u])
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs4(v,u);
        }
        sign[u]=0;
    }
    else
    {
        for(rg int i=head_[u];i;i=nxt_[i])
        {
            const int v=tow_[i];
            if(v!=fa)dfs4(v,u);
        }
        belo[u]=0;
    }
    head_[u]=0,Dis[u]=0x7f7f7f7f;
}
int main()
{
    memset(Dis,0x7f,sizeof(Dis));
    bit[0]=1;
    for(rg int i=1;i<=18;i++)bit[i]=bit[i-1]<<1;
    read(n),s=1;
    for(rg int i=1;i<n;i++)
    {
        int u,v;read(u),read(v);
        addb(u,v),addb(v,u);
    }
    who[s][0]=s,dep[s]=1;
    dfs(s);
    for(rg int j=1;j<=18;j++)
        for(rg int i=1;i<=n;i++)
        	who[i][j]=who[who[i][j-1]][j-1],dis[i][j]=dis[i][j-1]+dis[who[i][j-1]][j-1];
    read(m);
    for(rg int i=1;i<=m;i++)
    {
        read(k);
        for(rg int j=1;j<=k;j++)read(h[j]),qu[j]=h[j],sign[h[j]]=1,Dis[h[j]]=0;
        std::sort(h+1,h+k+1,cmp);
        tmp_=0;
        top=0,stack[++top]=h[1];
        for(rg int j=2;j<=k;j++)
        {
            const int LCA=lca(stack[top],h[j]);
            while(top>1&&dep[stack[top-1]]>dep[LCA])
            {
                const int DIS=dist(stack[top-1],stack[top]);
                addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);
                top--;
            }
            if(dep[stack[top]]>dep[LCA])
            {
                const int DIS=dist(LCA,stack[top]);
                addb_(LCA,stack[top],DIS),addb_(stack[top],LCA,DIS);
                top--;
            }
            if(!top||dep[stack[top]]<dep[LCA])stack[++top]=LCA;
            stack[++top]=h[j];
        }
        while(top>1)
        {
            const int DIS=dist(stack[top-1],stack[top]);
            addb_(stack[top-1],stack[top],DIS),addb_(stack[top],stack[top-1],DIS);
            top--;
        }
        dfs1_0(stack[1],0);
        dfs1_1(stack[1],0);
        dfs2(stack[1],0);
        dfs3(stack[1],0);
        dfs4(stack[1],0);
        for(rg int i=1;i<=k;i++)print(ans[qu[i]]),putchar(' ');
        putchar('\n');
    }
    return flush(),0;
}
总结

上面两道例题差不多覆盖了差不多虚树要用到的所以基础操作了,总结一个虚树的板子还是挺好的,虚树写起来简单,也挺好用的,复杂度也挺优秀,值得好好研究一番
update by 2018.3:感谢Michael_Li对本篇博客的建议与指正
update by 2019.1:对部分公式进行了美化更新

勇攀数字高峰 摘要:该题目要求在数字地形图中寻找从最低点到最高点的所有严格递增登山路径。采用DFS深度优先搜索结合回溯算法,通过标记已访问位置并检查高度差限制(0<diff≤maxDiff)来遍历所有可能路径。算法首先确定起点(最低点)和终点(最高点),然后进行DFS搜索,每步移动需满足四个方向的合法移动且高度严格递增。最终输出符合条件的路径总数。提供了C++、Java和Python三种实现方案。 阅读详情

相关推荐

避坑指南:WSL2网络模式修改导致无法上网的解决方案(附备份还原技巧)

本文深入解析WSL2默认网络隔离机制,并提供三种安全的端口互通方案,重点推荐通过配置Windows防火墙规则实现稳定访问。同时,针对网络配置修改可能引发的无法上网问题,给出了从重置配置到系统级备份的完整恢复指南,确保开发环境安全可靠。

jj890的博客 781

日常学习——Virtual_Tree(虚树)

概念 虚树,顾名思义就是虚构的树,它是一种用来解决树上问题的算法,主要思想是只将原树上必要的点和它们的最近公共祖先取出来,构成一棵虚树,并保留他们在树上的相对关系。 引入 我们先来看一道题: 给定一棵n个点的树,每次询问给定一个大小为k的点集,你需要切掉一些边,使得点集中的点均不与1号点联通,而每条边都有被切掉所需的代价,你还要让总代价最小。\(2 \leqslant n \leqsla...

digangmeng2546的博客 679

人形机器人人机交互解决方案(2026完整版)

《人形机器人人机交互解决方案(2026完整版)》摘要 本项目针对2026年人形机器人产业爆发式增长需求,提出了一套完整的人机交互解决方案。方案包含十大核心模块(语音识别、情感识别、手势识别等),采用五层架构设计,实现多模态融合交互,响应时间≤32ms,准确率≥99%。覆盖制造业巡检、家庭服务等10大场景,适配20+主流机型。项目总投资4.1亿元,预期年营收8.2亿元,将申请专利120项,培养专业人才300人。通过技术创新解决当前交互体验生硬、安全可控性不足等痛点,助力我国人形机器人产业实现从"跟跑

随读手记 776

虚树教程

问题引入 SDOI2011消耗战 虚树 按照朴素的做法,就是对于每一个询问都跑一边DP。 \[ Dp[i] = Dp[ i ] + \min( Dp[Son],Cost[i,Son]) \,\,\,(Son不为关键点)\\ Dp[i] = Dp[ i ] + Cost[ i, Son ] \,\,\,(Son为关键点)\\ Dp[i] = INF\,\,\,(i为叶子) \] 这样时间...

TIANFENG_123456的博客 392

人工智能AI在软件测试和质量效能中的实际应用

人工智能在软件测试中应用,介绍chatGPT在测试中的应用,还有多个AI驱动的测试工具。

lindafang72的博客 1万+

手把手教你学simulink(43.2)--光伏发电场景示例:基于Simulink开发一个光伏发电系统仿真模型:采用神经网络控制策略进行优化

需求分析光伏发电系统(Photovoltaic System, PV)是将太阳能转换为电能的重要手段,广泛应用于分布式发电、并网发电和离网供电等领域。随着光伏技术的发展,如何提高光伏发电系统的效率和稳定性成为研究的重点。传统的控制策略(如PID控制器)虽然简单易实现,但在复杂工况下(如光照强度变化、温度波动等)难以达到最优性能。因此,引入先进的控制策略,如神经网络控制(Neural Network Control, NNC),可以显著提升系统的响应速度和鲁棒性。

MHD0815的博客 438

数据结构专题——虚树

虚树(virtual tree)的概念 虚树 是将一个树的点集的某一个子集,以及该子集中点的 LCALCALCA 的集合,一起所重构出来的一棵树 虚树的用途 在树型dp中,有时候没必要对整颗树进行dp,只用对某个子集构成的虚树进行dp,大大降低了时空复杂度 例题:P2495 [SDOI2011] 消耗战 [SDOI2011] 消耗战 题目描述 在一场战争中,战场由 nnn 个岛屿和 n−1n-1n−1 个桥梁组成,保证每两个岛屿间有且仅有一条路径可达。现在,我军已经侦查到敌军的总部在编号为 111 的岛屿,

m0_52766855的博客 3075

点云体积计算方法之一 附python代码

另外需要注意,这个只适合点云密集分布的场景来计算体积,比如树木扫描的体积;如果只是有表面积扫描的点云,该方法就不能计算整个物体的体积了。通过计算点云所占用的体素的数量来计算点云的近似体积。这是一个非常粗略的近似值,对于复杂的形状可能不准确。

三维点云技术探索 1888

虚树

虚树做法 虚树是把原树中少量的有效节点和他们两两的LCA拿出来,去除一些无效节点,从而降低复杂度。 如果有效节点是K个的话 加上LCA 虚树中的的点最多为min(n,2*K)个 建虚树的复杂度为O(k*logn) 虚树上树形dp的复杂度为O(k) 证明: 按dfn值从小到大加入有效节点 假设现在加入的节点为X 前面的节点为Y 再前面的节点为Z 如果X与Y的LCA为LCA1 ...

baichuan9723的博客 205

运行 ui5 serve 命令行报错的处理方法

如果大家运行 ui5 serve 命令行,报如下的错误消息: ui5 不是内部或外部命令,也不是可运行的程序: 只需要执行命令行,全局安装 ui5 cli(command line interface)这个命令行工具即可: npm install --global @ui5/cli 这个 ui5 cli 的入口执行文件,实际安装在了我 Node.js 的安装目录下: 更多Jerry的原创文章,尽在:“汪子熙”: ...

2007 年 ~ 2025 年,深耕 SAP 技术 18 年 1343

腾讯音乐 QQ音乐商业化客户端开发工程师:岗位深度解析与面试攻略

腾讯音乐QQ音乐商业化客户端开发工程师岗位要求3年以上Android开发经验,熟练掌握Kotlin/Java及跨端开发技术。核心职责包括QQ音乐客户端功能开发、架构优化、性能提升及技术攻关。岗位要求计算机相关本科以上学历,具备完整项目开发经验,熟悉TCP/IP等网络协议,能独立解决复杂技术问题。面试重点考察项目经验、架构设计、性能优化及新技术研究能力,建议候选人准备具体案例,量化成果,并熟悉Flutter等跨端技术。该职位强调技术深度与产品思维的结合,需展现扎实的编程基础、问题解决能力和团队协作精神。

郑伟强dev的专栏 366

浅谈虚树(虚仙人掌)

浅谈虚树

Demilly123的博客 1083

YOLOv11涨点改进 | Conv篇/小目标改进 | YOLOv11引入SPDConv卷积特征无损下采样模块,增加YOLOv11小目标P2检测层(助力小目标任务高效涨点)

本文提出了一种创新的SPDConv模块来优化YOLOv11模型,通过空间到深度(SPD)层和非步幅卷积层实现特征无损下采样。该方法有效解决了传统CNN中因跨步卷积和池化层导致的细粒度信息丢失问题,特别提升了小目标检测性能。实验表明,SPDConv在目标检测和图像分类任务中显著优于现有SOTA模型。文章详细介绍了模块原理、核心代码实现,并提供了YOLOv11集成SPDConv的具体方法,包括配置文件修改和P2层增强等优化技巧。该技术为计算机视觉任务提供了一种高效的特征保留方案。

Ai缝合怪、助力小伙伴高效发论文 586

虚树详解

虚树大意:通过建一棵只包含询问点和不超过询问点数减一个lca的树来减少点的个数,降低时间复杂度 这东西不难,常用于辅助树形dp,难点在于dp… 使用该算法的标志为不确定组的询问次数与给出的询问点数和 例如: 每次询问w个数,∑i=1qwi=300000\sum_{i=1}^qw_i=300000\quad∑i=1q​wi​=300000 算法: 构建过程十分简单,放心食用 举个例子:P2495 ...

tylon2006的博客 908

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

win10 跳过U8环境检测的IE WebControls.rar

win10 安装u8的时候 提示IE WebControls 不通过,这个工具可以欺骗u8的检查。 跳过U8环境检测的IE WebControls。提示导入失败后,重新检测通过。

虚树详细讲解

新学虚树,写篇文章仔细地回顾一下吧。。。这玩意花了挺长时间的。 什么是虚树 虚树常常被使用在树形 dpdpdp 中,就比如这题。当一次询问仅仅涉及到整颗树中少量结点时,为每次询问都对整棵树进行 dpdpdp 在时间上是不可接受的。此时,我们建立一颗仅仅包含部分关键结点的虚树,将非关键点构成的链简化成边或是剪去,在虚树上进行 dpdpdp 。 虚树包含所有的询问点及它们之间的 lcalcalca 。显然虚树的叶子节点必然是询问点,因此对于某次含有 kkk 个点的询问,虚树最多有 kk...

weixin_45630722的博客 3060

IDEA的使用指南

IDEA基于java的使用手册,可以最快速的上手,在使用中不断的丰富自己的使用技巧.

虚树+树形dp

虚树实际上是一颗浓缩子树;使用虚树的题目大部分具有查询的点有限,同时虚树构建的信息符合规则;做虚树的题目:步骤为先想出原树的暴力求解做法,然后构建虚树同时向其中添加有用信息(比如边权);虚树的构建过程:虚树的构建大致有两种,但是两种方式都与dfs序有关;首先解释为什么与dfs序有关我们可以知道,虚树的构建是求各个关键点两两的lca的过程。如果是两两求lca,暴力构建虚树的话,复杂度会达到k*k*logn;dfs序的出现可以将复杂度降为klogn;

2403_86761086的博客 1908

Data-Structures-and-Algorithm-Analysis-in-Java.pdf

作者: Weiss, Mark A. 出版社: Addison Wesley Data Structures and Algorithm Analysis in Java is an “advanced algorithms” book that fits between traditional CS2 and Algorithms Analysis courses. In the old ACM Curriculum Guidelines, this course was known as CS7. This text is for readers who want to learn good programming and algorithm analysis skills simultaneously so that they can develop such programs with the maximum amo...

虚树模板

学习:chenhuan001 模板借鉴:自为风月马前卒 虚树模板题:P2495 [SDOI2011]消耗战 思路:裸虚树题,建好虚树后直接在虚树上树形dp即可。 #include<bits/stdc++.h> #define ll long long #define P pair<int,int> #define mk make_pair using namesp...

ccsu_cat 324

虚树学习笔记

如何更快把LCA记录下来呢,我们可以把关键点按照dfn序排序,以此记录相邻的关键点的LCA,并把它们加入。从图中可以看出,我们需要记录关键点及连接他们的LCA,同时他们的祖先关系也没有改变。最后,在序列上枚举相邻的两个点,求得它们的LCA,并连接LCA(x,y),y。对于每个选定,要分别求: 这些新通道的代价和,新通道中代价最小、最大的是多少。对于一类树上DP问题,每次询问所需要查询的点为关键点,当关键点的个数与。为了提高使用关键点的效率,我们把信息浓缩,使大树变成一棵小树。如下图,红色是我们的关键点。

MYJ_aiie的博客 848

虚树学习小记

## 虚树是什么 虚树指在原树上选择需要的点和它们的$LCA$组成的一棵树。这样可以使在树DP时顶点数更少,从而减少时间复杂度。一般用于有多组数据且能保证所有数据访问的点的和不超过规定范围。

tanjunming2020的博客 1558

知识点 - 虚树

知识点 - 虚树 解决问题类型: 利用虚树,可以对于指定多组点集 SSS 的询问进行每组 O(∣S∣log2n+f(∣S∣))O(|S|log_2n+f(|S|))O(∣S∣log2​n+f(∣S∣)) 的回答,其中 f(x)f(x)f(x)指的是对于树上有 xxx 个点的情况下单组询问这个问题的时间复杂度。可以看到,这个复杂度基本上(除了那个 log2nlog_2nlog2​n以外)与 nnn ...

qq_41848675的博客 548

虚树——详解

暴力枚举 LCA,所以我们不难想到——首先将关键点按 DFS 序排序,然后排完序以后相邻的两个关键点(相邻指的是在排序后的序列中下标差值的绝对值等于 1)求一下 LCA,并把它加入虚树。也就是一个栈里相邻的两个节点在虚树上也是相邻的,而且栈是从底部到栈首单调递增的(指的是栈中节点 DFS 序单调递增),说白了就是某个节点的父亲就是栈中它下面的那个节点。同阶的,也就是说实际上一次询问中红色的点对于整棵树来说是很稀疏的,所以如果我们能让复杂度由红色点的总数来决定就好了。下图中,红色结点是我们选择的关键点。

w_w_wwwww的博客 925

虚树模板题

虚树是这样一种思想,很多时候一棵树里只有一些点是我们会用到的,如果我们只对这些点进行操作,复杂度可以接受,但是还有很多对答案完全没影响的点,如果把他们也加进来复杂度就太高了,所以一个很自然的想法是,如果把对答案有影响的点称为关键点,我们能不能建一颗只含关键点的树,然后在这个树上计算答案?这里还有个麻烦的地方是,虚树上两点间的边,可能是原始数上很多条边,我们断开虚树上的边,实际上是在原始树上多条边中选一条断开,那显然应该选边权最小的那个,那么我们建树的时候,每次把。为根的子树内所有集合中的点,都到不了。

Maxwell_Newton的博客 839
上一篇: 第一类斯特林数学习记录
下一篇: 最小费用最大流-SPFA-多路增广
zhouyuheng2003
博客等级 码龄9年 84粉丝 111原创
评论 4
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值