虚树学习笔记
虚树学习笔记
虚树是用来优化树形
前置知识:在线
P2495 [SDOI2011] 消耗战
题目描述
在一场战争中,战场由
侦查部门还发现,敌军有一台神秘机器。即使我军切断所有能源之后,他们也可以用那台机器。机器产生的效果不仅仅会修复所有我军炸毁的桥梁,而且会重新随机资源分布(但可以保证的是,资源不会分布到
输入格式
第一行一个整数
接下来
第
接下来
输出格式
输出共
样例 #1
样例输入 #1
10
1 5 13
1 9 6
2 1 19
2 4 8
2 3 91
5 6 8
7 5 4
7 8 31
10 7 9
3
2 10 6
4 5 7 8 3
3 9 4 6
样例输出 #1
12
32
22
提示
数据规模与约定
- 对于
的数据, 。 - 对于
的数据, 。 - 对于
的数据, 。 - 对于
的数据, 。
朴素做法
最简单的做法就是对每一次询问都进行一次树形
令
不难发现,这样的总时间复杂度为
虚树
观察每次询问,会发现每次
虚树就是从原来的树中抽离出来的一部分,由我们需要的关键点和这些关键点的
对于建树的方法,
建立虚树
在倍增求
首先需要明确,只要满足虚树中的节点的祖孙关系与原树保持一致,虚树中的节点多少并不会影响答案。其实也就是说,只要愿意,可以把所有点都加入虚树(如果你真的这么做,那就是你太闲了owo),也不会导致答案出现错误(只是时间 。
所以为了我们处理方便,首先先把根节点
维护一个栈,栈中的元素即是一条链上的节点。
对于一个新的关键点
如果
建树代码:
bool cmp(int x,int y) {return id[x]<id[y];}
void build()
{
sort(h+1,h+k+1,cmp);
s[top=1]=1,tot=0,head[1]=0;
for (int i=1;i<=k;i++)
if (h[i]!=1)
{
int lca=LCA(h[i],s[top]);
if (lca!=s[top])
{
while (id[lca]<id[s[top-1]])
AddEdge(s[top-1],s[top],query(s[top-1],s[top])),top--;
if (id[lca]!=id[s[top-1]])
head[lca]=0,AddEdge(lca,s[top],query(lca,s[top])),s[top]=lca;
else
AddEdge(lca,s[top],query(lca,s[top])),top--;
}
head[h[i]]=0,s[++top]=h[i];
}
for (int i=1;i<top;i++) AddEdge(s[i],s[i+1],query(s[i],s[i+1]));
}
建好虚树过后,就可以直接在建好的虚树上跑我们刚刚的树形
完整代码
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof a)
//#define int long long
using namespace std;
template<typename T> void read(T &k)
{
k=0;T flag=1;char b=getchar();
while (!isdigit(b)) {flag=(b=='-')?-1:1;b=getchar();}
while (isdigit(b)) {k=k*10+b-48;b=getchar();}
k*=flag;
}
template<typename T> void write(T k) {if (k<0) {putchar('-'),write(-k);return;}if (k>9) write(k/10);putchar(k%10+48);}
template<typename T> void writewith(T k,char c) {write(k);putchar(c);}
const int _SIZE=2.5e5;
int n,m,k,h[_SIZE+5],id[_SIZE+5];
struct EDGE{
int nxt,to,w;
}edge[(_SIZE<<1)+5];
int tot,head[_SIZE+5];
void AddEdge(int x,int y,int w) {edge[++tot]=(EDGE){head[x],y,w};head[x]=tot;}
int dep[_SIZE+5],f[_SIZE+5][35],g[_SIZE+5][35],cnt;
void dfsForLCA(int x,int fa,int w)//预处理LCA和DFS序
{
f[x][0]=fa;dep[x]=dep[fa]+1;g[x][0]=w;id[x]=++cnt;
for (int i=1;i<=30;i++) f[x][i]=f[f[x][i-1]][i-1],g[x][i]=min(g[f[x][i-1]][i-1],g[x][i-1]);
for (int i=head[x];i;i=edge[i].nxt) if (edge[i].to!=fa) dfsForLCA(edge[i].to,x,edge[i].w);
}
int LCA(int x,int y)//求LCA
{
if (dep[x]<dep[y]) swap(x,y);
int temp=dep[x]-dep[y];
for (int i=0;temp;i++,temp>>=1)
if (temp&1) x=f[x][i];
if (x==y) return x;
for (int i=30;i>=0;i--)
if (f[x][i]!=f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
int query(int x,int y)//求路径长
{
if (dep[x]<dep[y]) swap(x,y);
int temp=dep[x]-dep[y],ans=INT_MAX;
for (int i=0;temp;i++,temp>>=1) if (temp&1) ans=min(ans,g[x][i]),x=f[x][i];
return ans;
}
bool cmp(int x,int y) {return id[x]<id[y];}
int s[_SIZE+5],top;
void build()
{
sort(h+1,h+k+1,cmp);
s[top=1]=1,tot=0,head[1]=0;
for (int i=1;i<=k;i++)
if (h[i]!=1)
{
int lca=LCA(h[i],s[top]);
if (lca!=s[top])
{
while (id[lca]<id[s[top-1]])
AddEdge(s[top-1],s[top],query(s[top-1],s[top])),top--;
if (id[lca]!=id[s[top-1]])
head[lca]=0,AddEdge(lca,s[top],query(lca,s[top])),s[top]=lca;
else
AddEdge(lca,s[top],query(lca,s[top])),top--;
}
head[h[i]]=0,s[++top]=h[i];
}
for (int i=1;i<top;i++) AddEdge(s[i],s[i+1],query(s[i],s[i+1]));
}
int dp[_SIZE+5];
bool flag[_SIZE+5];
void DP(int x,int fa)
{
for (int i=head[x];i;i=edge[i].nxt)
{
int twd=edge[i].to;
if (twd==fa) continue;
DP(twd,x);
if (flag[twd]) dp[x]+=edge[i].w;
else dp[x]+=min(edge[i].w,dp[twd]);
dp[twd]=0,flag[twd]=0;
}
}
signed main()
{
read(n);mem(g,0x3f);
for (int i=1;i<n;i++)
{
int u,v,c;read(u),read(v),read(c);
AddEdge(u,v,c),AddEdge(v,u,c);
}
dfsForLCA(1,0,0);
read(m);
for (int i=1;i<=m;i++)
{
read(k);
for (int j=1;j<=k;j++) read(h[j]),flag[h[j]]=1;
build();
DP(1,0);
writewith(dp[1],'\n');
flag[1]=0,dp[1]=0;
}
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步