虚树
虚树
简介
如果题目中有这样子的字眼,给你 q 个询问,每个询问给一些点,这些点的总和不多,但是总的点数很多,意思就是绝大部分点用不到,这样子的情况,就考虑用虚树去优化。
一般的步骤
1.dfs预处理出LCA需要用到的数组,顺便搞出DFS序
// 经典的 两件套去搞出 LCA
void dfs1(int u,int pr)
{
de[u]=de[pr]+1;
fa[u][0]=pr;
id[u]=++idx;//顺便搞出dfs
for(int i=1;i<26;i++)fa[u][i]=fa[fa[u][i-1]][i-1],mn[u][i]=min(mn[u][i-1],mn[fa[u][i-1]][i-1]);
for(pi t2:v[u])
{
int j=t2.x,y=t2.y;
if(j==pr)continue;
mn[j][0]=y;
dfs1(j,u);
}
}
int get(int x,int y)
{
cc=INF;//这里是加了一步,要搞出这个路径上的最小值。
if(de[x]<de[y])swap(x,y);
for(int j=25;j>=0;j--)
{
if(de[fa[x][j]]>=de[y])
{
cc=min(cc,mn[x][j]);
x=fa[x][j];
}
}
if(x==y)return x;
for(int j=25;j>=0;j--)
{
if(fa[x][j]!=fa[y][j])
{
cc=min(cc,min(mn[x][j],mn[y][j]));
x=fa[x][j];
y=fa[y][j];
}
}
return fa[x][0];
}
2.build
先将这一个询问中的点按照dfs序列排序,方便后面操作。然后用一个栈来模拟这个建新边的过程,每次都求一下LCA(当前点和栈顶元素)。
然后对于这个Lca 有三种情况,这里面每一次的get 是为了找出这条链上的最小值,从而方便建边
- 和栈顶相同,2. dfs序比栈中第二个的dfs序小, 3.dfs序比栈中第二个的dfs序大。
1. 和栈顶相同
// g是新边 h数组是本次询问的点 stk是栈,
// 情况1 的话说明这个点是第一次被放入,所以捏,要清空一下数组,同时入栈。
g[h[i]].clear();stk[++top]=h[i];
// 这个操作每一次都会进入。表示这个点在栈中。
2.dfs序比栈中第二个的dfs序小
//这里需要用到while 因为每次要知道遇到情况1为止。
//对于这个栈的话,我们在栈的第二个点连一条边到栈顶。
while(id[f]<id[stk[top-1]])
{
int u=stk[top-1],v=stk[top];get(u,v);
g[u].ps({v,cc}),top--;
}
3.dfs序比栈中第二个的dfs序大。
// 这一段的意思就是,如果我的f 比栈中第二个的dfs 大,
//说明这个Lca没有进去过,要考虑怎么把这个Lca放进去,也就是 连一条从f到 栈顶的边
if(id[f]>id[stk[top-1]])
{
g[f].clear();
int u=f,v=stk[top];get(u,v);
g[u].ps({v,cc}),top--;
stk[++top]=f;
}
4.最后清空一下stk
while(top>1)
{
int u=stk[top-1],v=stk[top];
get(u,v);
g[u].ps({v,cc}),top--;
}
return;
最后愉快的在这个新树上Dp
消耗战
题意:
给你 q 个询问,每个询问一些点,然后问这些点中,怎么删边能用最小的边权和 让所有给定的点和 1 号点不联通。
思路:
都把这个作为虚树的板子题了,还能怎么写 (O.o)!! 先考虑暴力,如果一个点的儿子一定要删除,那么这个点的权值+w,如果不一定删除,那么就考虑min (删除当前这条边,子树中的一个个删除)。
void dfs(int u,int pr)
{
for(pi t2:v[u])
{
int j=t2.x,w=t2.y;
if(j==pr)continue;
dfs(j,u);
if(is[j])d[u]+=w;
else d[u]+=min(d[j],w);
}
正解也就是在这个模型上 加上虚树去减少这个点的数量,同时用Lca 去快速找出一条链上的最小值。
Code
const int N=3e5+100,mod=1e9+7,INF=1e10;
int lowbit(int x){return x&-x;}
int gcd(int a,int b){return a%b==0?b:gcd(b,a%b);}
int d[N];
vector<pi>v[N];
vector<pi>g[N];
int is[N],id[N],idx,fa[N][26],de[N];
set<int>s;
int w[N],h[N],k,stk[N],dfn[N];
int cc,mn[N][26];
void add(int a,int b,int c)
{
v[a].ps({b,c});
}
bool cmp(int x,int y)
{
return id[x]<id[y];
}
void build()
{
cin>>k;
s.clear();
for(int i=1;i<=k;i++)
{
cin>>h[i];
s.insert(h[i]);
}
sort(h+1,h+1+k,cmp);
int top=0;
stk[++top]=1;g[1].clear();
for(int i=1;i<=k;i++)
{
// if(h[i]==1)continue;
// cout<<h[i]<<endl;
int f=get(h[i],stk[top]);
if(f!=stk[top])
{
while(id[f]<id[stk[top-1]])
{
int u=stk[top-1],v=stk[top];get(u,v);
g[u].ps({v,cc}),top--;
}
if(id[f]>id[stk[top-1]])
{
g[f].clear();
int u=f,v=stk[top];get(u,v);
g[u].ps({v,cc}),top--;
stk[++top]=f;
}
else
{
int u=stk[top-1],v=stk[top];
get(u,v);
g[u].ps({v,cc}),top--;
}
}
g[h[i]].clear();stk[++top]=h[i];
}
while(top>1)
{
int u=stk[top-1],v=stk[top];
get(u,v);
g[u].ps({v,cc}),top--;
}
return;
}
void dfs2(int u,int pr)
{
d[u]=0;
// cout<<u<<endl;
for(pi t2:g[u])
{
int j=t2.x,y=t2.y;
if(j==pr)continue;
dfs2(j,u);
if(s.count(j))d[u]+=y;
else d[u]+=min(y,d[j]);
}
}
void solve()
{
int n;cin>>n;
memset(mn,0x3f,sizeof mn);
for(int i=1;i<n;i++)
{
int a,b,c;cin>>a>>b>>c;
add(a,b,c);add(b,a,c);
}
dfs1(1,1);
int q;cin>>q;
while(q--)
{
build();
dfs2(1,-1);
cout<<d[1]<<endl;
}
}
signed main()
{
kd;
int _;_=1;
//cin>>_;
while(_--)solve();
return 0;
}

浙公网安备 33010602011771号