虛樹模板詳解和例題

2021-09-03 02:23:35 字數 3955 閱讀 5767

虛樹:原樹中給一些點,通過一些lca把它們重新連線在一起建成一棵新樹,相當於對樹進行了化簡。新樹邊上的資訊可以用樹鏈剖分/倍增等維護。

如何建虛樹:

假設給出的點都存在d陣列裡。

對於這些點,按照它們在原樹中的dfs序從小到大排個序。

然後我們建乙個棧,從棧底到棧頂相當於是一條當前虛樹根一直往下走的鏈。

遍歷d陣列,假設當前要加入的點是v,棧頂的點是u,

1.lca(u,v)==u 顯然u還可以往下走,這條鏈還沒完,直接把v入棧,continue。

2.lca(u,v)!=u 這代表著u這顆子樹已經結束了,v是u子樹外的點。設lca(u,v)=w,我們沿著當前維護的這條鏈一直往上走,在棧上相當於是乙個彈棧+建邊的過程,代表著彈出的點往下的那條鏈已經結束了,不會再被更改,直到有乙個點是w或w的祖先為止。此時如果這個點是w則直接把v入棧,反之則先把w入棧,再把v入棧。

注意,入棧時不建邊而在出棧時建,就是為了出棧時鏈才成型的特性。

把dfs序從小到大排序,相當於我們是在dfs樹上中序遍歷了這些點,保證了演算法的正確性。

板子是我自己yy的,過了以下的兩題,如果在其他題目有鍋,歡迎指出qwq。

bzoj2286消耗戰(板子題,可以用來檢驗建樹模板是否正確)

#includeusing namespace std;

#define rep(x,y,z) for (register int x=y; x<=z; x++)

#define downrep(x,y,z) for (register int x=y; x>=z; x--)

#define ms(x,y,z) memset(x,y,sizeof(z))

#define ll long long

#define repedge(x,y) for (register int x=hed[y]; ~x; x=edge[x].nex)

#define repe(x,y) for(register int x=head[y]; ~x; x=e[x].nex)

inline int read()

const int n=250005;

const int maxlg=18;

const int inf=n+1;

int n,m,tin[n],tout[n],tot,dep[n],toc[n],sum,f[n][maxlg+1],g[n][maxlg+1];

int nedge,hed[n],nedge,head[n],d[n],imp[n],st[n];

ll dp[n];

struct edgeedge[n<<1],e[n<<1];

void addedge(int a,int b,int c)

void dfs_1(int k)tout[k]=tot;

}void dfs_2(int k)

}int cmp(int a,int b)

while(tp>1) adde(st[tp-1],st[tp]),tp--;

}int main()

f[1][0]=1; g[1][0]=inf; dfs_1(1);

rep(j,1,maxlg) rep(i,1,n)

scanf("%d",&m); nedge=0; ms(head,-1,head);

rep(i,1,m)

return 0;

}

bzoj3572世界樹(細節賊多的虛樹裸題,不過想清楚再寫就不難了)

具體題解見我的luogu部落格

#includeusing namespace std;

#define rep(x,y,z) for (register int x=y; x<=z; x++)

#define downrep(x,y,z) for (register int x=y; x>=z; x--)

#define ms(x,y,z) memset(x,y,sizeof(z))

#define ll long long

#define repedge(x,y) for (register int x=hed[y]; ~x; x=edge[x].nex)

#define repe(x,y) for(register int x=head[y]; ~x; x=e[x].nex)

#define mp make_pair

#define pr pair#define fr first

#define se second

inline int read()

const int n=300005;

const int maxlg=18;

int n,m,nedge,hed[n],sz[n],tin[n],tout[n],dep[n],f[n][maxlg+1],tot;

int nedge,head[n],d[n],st[n],tmp[n],imp[n],toc[n],sum,val[n],ans[n],lg[(1<1)&&(dep[st[tp-1]]>dep[w])) adde(st[tp-1],st[tp]),tp--;

adde(w,st[tp]); tp--; if ((!tp)||(st[tp]!=w)) st[++tp]=w; st[++tp]=v;

} while(tp>1) adde(st[tp-1],st[tp]),tp--; return st[tp];

}void dfs_2(int k)

}void dfs_3(int k)

}int main()

rep(i,0,maxlg) lg[(1直接虛樹+dp就可以了。

#includeusing namespace std;

#define rep(x,y,z) for (register int x=y; x<=z; x++)

#define downrep(x,y,z) for (register int x=y; x>=z; x--)

#define ms(x,y,z) memset(x,y,sizeof(z))

#define ll long long

#define repedge(x,y) for (register int x=hed[y]; ~x; x=edge[x].nex)

#define repe(x,y) for (register int x=head[y]; ~x; x=e[x].nex)

inline int read()

const int n=1000005;

const int maxlg=20;

int n,m,nedge,hed[n],tot,tin[n],tout[n],fa[n][maxlg+1],dep[n];

int nedge,head[n],toc[n],cnt,d[n],imp[n],st[n];

ll sum[n],calc[n],len[n],f[n],f2[n],g[n],g2[n];

struct edgeedge[n<<1];

struct nodee[n];

void addedge(int a,int b)

void dfs_1(int k)tout[k]=tot;

}bool isancestor(int x,int y)

int getlca(int x,int y)

int cmp(int x,int y)

while(tp>1) adde(st[tp-1],st[tp]),tp--; return st[tp];

}const ll inf=1e9;

void dfs_2(int k)

}int main()

fa[1][0]=1; dfs_1(1);

rep(j,1,maxlg) rep(i,1,n) fa[i][j]=fa[fa[i][j-1]][j-1];

scanf("%d",&m); nedge=0; ms(head,-1,head);

rep(i,1,m)

return 0;

}

線段樹 模板 例題

模板 以區間和為例。ll ls ll p ll rs ll p void push up ll p void build ll p,ll l,ll r ll mid l r 1 build ls p l,mid build rs p mid 1 r push up p void f ll p,ll ...

字典樹模板及例題

字典樹 time limit 1000ms memory limit 65536kb problem description 遇到單詞不認識怎麼辦?查字典啊,已知字典中有n個單詞,假設單詞都是由小寫字母組成。現有m個不認識的單詞,詢問這m個單詞是否出現在字典中。input 含有多組測試用例。第一行輸...

全排列 模板和例題

1.直接使用 stl 中的 next permutation 實現全排列 includeusing namespace std intmain sort a,a 4 do printf n while next permutation a,a 4 2.遞迴求全排列 n 個元素的全排列 不斷將每個元素...