[学习笔记]虚树

模板:(树剖\(LCA\)+建虚树)

#include 
using namespace std;
const int maxn=100000+10;
int n,m,dp[maxn],vis[maxn],h[maxn],sta[maxn],Top;
int fir[maxn],head[maxn],to[maxn<<1],nxt[maxn<<1],tot,cnt;
int top[maxn],dep[maxn],id[maxn],siz[maxn],son[maxn],fa[maxn],tim;

struct node{
    int to,next;
}e[maxn<<1];

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}
inline void add(int x,int y){
    e[++tot].to=y;
    e[tot].next=head[x];
    head[x]=tot;
}
inline void addedge(int x,int y){
    to[++cnt]=y;
    nxt[cnt]=fir[x];
    fir[x]=cnt;
}

void dfs1(int x,int f){
    siz[x]=1;fa[x]=f;
    dep[x]=dep[f]+1;
    int maxson=-1;
    for(int i=head[x],y;i;i=e[i].next){
        y=e[i].to;
        if(y==f) continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(maxsondep[y]) swap(x,y);
    return x;
}

bool cmp(int a,int b){
    return id[a]

具体建虚树怎么建可以看别人的博客……我讲的肯定没有它们好

1、[SDOI2011]消耗战

分析:人生第一道虚树题。

难的就是建一棵虚树,然后在虚树上树形 \(dp\)

首先,打出一个树上前缀最小值。因为无论怎样,选一条最小的边断掉一定是最优的。建一棵虚树,若遍历到选定的点 \(x\),那么 \(dp[x]=min(dis[x],\sum_{son\in x}val_{x->son})\),其中 \(val\) 为边权。

\(Code\ Below:\)

#include 
#define int long long
using namespace std;
const int maxn=250000+10;
const int inf=1e18;
int n,m,dp[maxn],dis[maxn],vis[maxn],h[maxn],sta[maxn],Top;
int fir[maxn],head[maxn],to[maxn<<1],nxt[maxn<<1],tot,cnt;
int top[maxn],dep[maxn],id[maxn],siz[maxn],son[maxn],fa[maxn],tim;

struct node{
    int to,next,val;
}e[maxn<<1];

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}
inline void add(int x,int y,int w){
    e[++tot].to=y;
    e[tot].val=w;
    e[tot].next=head[x];
    head[x]=tot;
}
inline void addedge(int x,int y){
    to[++cnt]=y;
    nxt[cnt]=fir[x];
    fir[x]=cnt;
}

void dfs1(int x,int f){
    siz[x]=1;fa[x]=f;
    dep[x]=dep[f]+1;
    int maxson=-1;
    for(int i=head[x],y;i;i=e[i].next){
        y=e[i].to;
        if(y==f) continue;
        dis[y]=min(dis[x],e[i].val);
        dfs1(y,x);
        siz[x]+=siz[y];
        if(maxsondep[y]) swap(x,y);
    return x;
}

bool cmp(int a,int b){
    return id[a]

2、[HEOI2014]大工程

分析:这道题自己推的,很有成就感哈哈哈

方法与上题一样,不过多一点细节

\(sub[x]\) 表示在虚树上 \(x\) 的子树内有多少个选定点

这些点对 \((x,y)\) 对答案的贡献要分两类讨论:

1、\(x=lca(x,y)\) ,那么直接在 \(vis[x]=1\) 的时候算掉

2、\(x,y\) 在两棵不同的子树内,那就一边更新 \(sub[x]\) 一边算

void dfs(int x){
    int now=0;
    for(int i=fir[x];i;i=nxt[i]){
        dfs(to[i]);
        now+=sub[x]*sub[to[i]];
        sub[x]+=sub[to[i]];
        sub[to[i]]=0;
    }
    ans-=2*now*dep[x];
    if(vis[x]) ans-=2*sub[x]*dep[x],sub[x]++;
}

找最小边权就是记录一下最小值和次小值,然后更新 \(ans\)

找最大边权同个道理

int dfs_min(int x){
    int Min=inf,sec=inf;
    for(int i=fir[x];i;i=nxt[i]){
        sec=min(sec,dfs_min(to[i]));
        if(Min>sec) swap(Min,sec);
    }
    if(vis[x]&&Min!=inf) ans=min(ans,Min-dep[x]);
    if(Min!=inf&&sec!=inf) ans=min(ans,Min+sec-2*dep[x]);
    if(vis[x]) Min=dep[x];
    return Min;
}

int dfs_max(int x){
    int Max=-inf,sec=-inf;
    for(int i=fir[x];i;i=nxt[i]){
        sec=max(sec,dfs_max(to[i]));
        if(Max

那个前式链向星数组 \(fir[x]\) 一定要在 \(dfsmax()\) 的时候清空!!!

\(Code\ Below:\)

#include 
#define int long long
using namespace std;
const int maxn=1000000+10;
const int inf=1e18;
int n,m,dp[maxn],vis[maxn],h[maxn],sub[maxn],sta[maxn],Top;
int fir[maxn],head[maxn],to[maxn<<1],nxt[maxn<<1],tot,cnt;
int top[maxn],dep[maxn],id[maxn],siz[maxn],son[maxn],fa[maxn],tim,ans;

struct node{
    int to,next;
}e[maxn<<1];

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}
inline void add(int x,int y){
    e[++tot].to=y;
    e[tot].next=head[x];
    head[x]=tot;
}
inline void addedge(int x,int y){
    to[++cnt]=y;
    nxt[cnt]=fir[x];
    fir[x]=cnt;
}

void dfs1(int x,int f){
    siz[x]=1;fa[x]=f;
    dep[x]=dep[f]+1;
    int maxson=-1;
    for(int i=head[x],y;i;i=e[i].next){
        y=e[i].to;
        if(y==f) continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(maxsondep[y]) swap(x,y);
    return x;
}

bool cmp(int a,int b){
    return id[a]sec) swap(Min,sec);
    }
    if(vis[x]&&Min!=inf) ans=min(ans,Min-dep[x]);
    if(Min!=inf&&sec!=inf) ans=min(ans,Min+sec-2*dep[x]);
    if(vis[x]) Min=dep[x];
    return Min;
}

int dfs_max(int x){
    int Max=-inf,sec=-inf;
    for(int i=fir[x];i;i=nxt[i]){
        sec=max(sec,dfs_max(to[i]));
        if(Max

3、CF613D Kingdom and its Cities

分析:在虚树上树形 \(dp\) 的时候分三种情况:

\(P.S:sum\) 表示有多少个儿子已经选了

1、\(vis[x]=1\),那么不能选 \(x\) 来断掉儿子的退路,那么 \(dp[x]=\sum_{son\in x} dp[son]\)

2、\(vis[x]=0,sum>1\),那就直接选 \(x\)\(x\) 的子树已经被 \(x\) 封死了

3、\(vis[x]=0,sum\leq 1\),那就传到 \(x\) 的父亲上,让 \(x\) 的父亲解决好了

\(Code\ Below:\)

#include 
using namespace std;
const int maxn=100000+10;
int n,m,dp[maxn],vis[maxn],h[maxn],sta[maxn],Top;
int fir[maxn],head[maxn],to[maxn<<1],nxt[maxn<<1],tot,cnt;
int top[maxn],dep[maxn],id[maxn],siz[maxn],son[maxn],fa[maxn],tim;

struct node{
    int to,next;
}e[maxn<<1];

inline int read(){
    register int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)){if(ch=='-')f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
    return (f==1)?x:-x;
}
inline void add(int x,int y){
    e[++tot].to=y;
    e[tot].next=head[x];
    head[x]=tot;
}
inline void addedge(int x,int y){
    to[++cnt]=y;
    nxt[cnt]=fir[x];
    fir[x]=cnt;
}

void dfs1(int x,int f){
    siz[x]=1;fa[x]=f;
    dep[x]=dep[f]+1;
    int maxson=-1;
    for(int i=head[x],y;i;i=e[i].next){
        y=e[i].to;
        if(y==f) continue;
        dfs1(y,x);
        siz[x]+=siz[y];
        if(maxsondep[y]) swap(x,y);
    return x;
}

bool cmp(int a,int b){
    return id[a]1) dp[x]=0,ans++;
    else dp[x]=sum;
    fir[x]=0;
    return ans;
}

int main()
{
    n=read();
    int x,y,w,k,lca,flag;
    for(int i=1;i

转载于:https://www.cnblogs.com/owencodeisking/p/9965535.html

你可能感兴趣的:([学习笔记]虚树)