[浅谈] 虚树
所谓虚树,就是对树上
建树复杂度
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=2.5e5+110,M=1e6+110;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
struct Tree_edge{
int head[N],last[M],to[M],w[M],tot;
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
}E1,E2;
int n,dep[N],dfn[N],CntDfn,f[N][21],mw[N][21];//2^18>2.5e5
void dfs(int u,int h){
dfn[u]=++CntDfn;dep[u]=h;
for(int i=E1.head[u];i;i=E1.last[i]){
int v=E1.to[i];if(v==f[u][0])continue;
f[v][0]=u,mw[v][0]=E1.w[i],dfs(v,h+1);
}
return;
}
int LCApos,LCAva;
void LCA(int x,int y){
LCAva=2e9;
if(dep[x]<dep[y])swap(x,y);
int de=dep[x]-dep[y];
for(int i=18;i>=0;i--)
if(de&(1<<i))LCAva=min(LCAva,mw[x][i]),x=f[x][i];
for(int i=18;i>=0;i--)
if(f[x][i]!=f[y][i]){
LCAva=min(LCAva,mw[x][i]),x=f[x][i];
LCAva=min(LCAva,mw[y][i]),y=f[y][i];
}
if(x!=y){
LCAva=min(LCAva,mw[x][0]),x=f[x][0];
LCAva=min(LCAva,mw[y][0]),y=f[y][0];
}
LCApos=x;return;
}
int m,keypos[N],st[M],top;
int rcd[N],lenrcd;int dp[N],vis[N];
bool cmp(int A,int B){return dfn[A]<dfn[B];}
void DP(int u){
if(vis[u]){dp[u]=2e17;return;}
dp[u]=0;
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];DP(v);
dp[u]+=min(dp[v],E2.w[i]);
}
return;
}
void solve(){
top=0;for(int i=1;i<=lenrcd;i++)E2.head[rcd[i]]=0;E2.tot=0;lenrcd=0;
int k=read();for(int i=1;i<=k;i++)keypos[i]=read(),vis[keypos[i]]=1;
sort(keypos+1,keypos+k+1,cmp);
for(int i=1;i<k;i++){
st[++top]=keypos[i];
LCA(keypos[i],keypos[i+1]);st[++top]=LCApos;
}st[++top]=keypos[k];
sort(st+1,st+top+1,cmp);
top=unique(st+1,st+top+1)-st-1
for(int i=1;i<top;i++){
LCA(st[i],st[i+1]);int tmp=LCApos;
LCA(tmp,st[i+1]);
E2.add(tmp,st[i+1],LCAva);rcd[++lenrcd]=tmp;
}
if(st[1]!=1){
LCA(1,st[1]);
E2.add(1,st[1],LCAva),rcd[++lenrcd]=1;
}
DP(1);printf("%lld\n",dp[1]);
for(int i=1;i<=k;i++)vis[keypos[i]]=0;
return;
}
signed main(){
n=read();
for(int i=1;i<n;i++){
int u=read(),v=read(),t=read();
E1.add(u,v,t),E1.add(v,u,t);
}
dfs(1,1);
for(int i=1;i<=18;i++)
for(int j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1],
mw[j][i]=min(mw[j][i-1],mw[f[j][i-1]][i-1]);
m=read();for(int i=1;i<=m;i++)solve();
return 0;
}
首先看到
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int N=1e6+110,M=2e6+110;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
struct Tree_edge{
int tot,head[N],to[M],w[M],last[M];
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
}E1,E2;
int n,f[N][21],dfn[N],dep[N],cntdfn;//2^20>1e6
void dfs(int u,int h){
dep[u]=h,dfn[u]=++cntdfn;
for(int i=E1.head[u];i;i=E1.last[i]){
int v=E1.to[i];if(v==f[u][0])continue;
f[v][0]=u;dfs(v,h+1);
}
return;
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int de=dep[x]-dep[y];
for(int i=20;i>=0;i--)
if(de&(1ll<<i))x=f[x][i];
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
if(x!=y)x=f[x][0],y=f[y][0];
return x;
}
int dp2[N],dp3[N],k,rcd[N],cntrcd,st[M],top,ans1,ans2,ans3,vis[N],keypos[N];
bool cmp(int A,int B){return dfn[A]<dfn[B];}
int DP1(int u){
int sz=vis[u];
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];int tmp=DP1(v);
sz+=tmp;
ans1+=tmp*(k-tmp)*E2.w[i];
}
return sz;
}
void DP2(int u){
dp2[u]=(!vis[u])*2e12;
dp3[u]=(!vis[u])*(-2e12);
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];DP2(v);
ans2=min(ans2,E2.w[i]+dp2[v]+dp2[u]);
ans3=max(ans3,E2.w[i]+dp3[v]+dp3[u]);
dp2[u]=min(dp2[u],dp2[v]+E2.w[i]);
dp3[u]=max(dp3[u],dp3[v]+E2.w[i]);
}
}
void solve(){
E2.tot=0;for(int i=1;i<=cntrcd;i++)E2.head[rcd[i]]=0;top=0;ans1=0,ans2=2e12,ans3=-2e12;cntrcd=0;//栈忘记清空会T
k=read();for(int i=1;i<=k;i++)keypos[i]=read(),vis[keypos[i]]=1;
sort(keypos+1,keypos+k+1,cmp);
for(int i=1;i<k;i++)
st[++top]=keypos[i],st[++top]=LCA(keypos[i],keypos[i+1]);
st[++top]=keypos[k];
sort(st+1,st+top+1,cmp);
top=unique(st+1,st+top+1)-st-1;
for(int i=1;i<top;i++){
int tmp=LCA(st[i],st[i+1]);
E2.add(tmp,st[i+1],dep[st[i+1]]-dep[tmp]),rcd[++cntrcd]=tmp;
}
DP1(st[1]);DP2(st[1]);printf("%lld %lld %lld\n",ans1,ans2,ans3);
for(int i=1;i<=k;i++)vis[keypos[i]]=0;
return;
}
signed main(){
n=read();
for(int i=1;i<n;i++){int u=read(),v=read();E1.add(u,v,1);E1.add(v,u,1);}
dfs(1,1);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1];
int m=read();for(int i=1;i<=m;i++)solve();
return 0;
}
其实虚树模板打完就是普通的树形
设
然后分类讨论:
- 这个点是重要城市,
(自己到自己也算一条路径),
:对于一个子树,可以让它有 条路径(不然就和 连起来了),或者把中间切断(如果能切)然后随它又没有路径。
if(E2.w[i]) g[u][1]+=min(g[v][0],1ll+min(g[v][1],g[v][0]));//如果到自己的儿子中间夹有其他普通城市,那么边权为1
else g[u][1]+=g[v][0];
2.这个点不是重要城市。
因为不是重要城市,所以可以将它自己切断,那么就与子树肯定没有连边了,所以最多只要再切一次(切自己)。
如果切:
long long tmp=1;
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];tmp+=min(g[v][1],g[v][0]);
}
如果不切:
long long de=0;
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];dp(v);
g[u][0]+=g[v][0];
de=max(de,g[v][0]-g[v][1]);
}
g[u][1]=g[u][0]-de;//可以保留最多一条到重要城市的路径
然后这题就结束了。
点击查看代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+100,M=2e5+110;
int read(){
int x=0,f=1;char c=getchar();
while(c>'9' || c<'0'){if(c=='-')f=-1;c=getchar();}
while(c>='0' && c<='9'){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
return x*f;
}
struct Tree_edge{
int tot,head[N],to[M],last[M],w[M];
void add(int u,int v,int t){to[++tot]=v,w[tot]=t,last[tot]=head[u],head[u]=tot;return;}
}E1,E2;
int n,dep[N],cntdfn,dfn[N],f[N][21];//2^20>>1e5
void dfs(int u,int h){
dep[u]=h,dfn[u]=++cntdfn;
for(int i=E1.head[u];i;i=E1.last[i]){
int v=E1.to[i];if(v==f[u][0])continue;
f[v][0]=u;dfs(v,h+1);
}
return;
}
int LCA(int x,int y){
if(dep[x]<dep[y])swap(x,y);
int de=dep[x]-dep[y];
for(int i=20;i>=0;i--)
if(de&(1ll<<i))x=f[x][i];
for(int i=20;i>=0;i--)
if(f[x][i]!=f[y][i])x=f[x][i],y=f[y][i];
if(x!=y)x=f[x][0],y=f[y][0];
return x;
}
bool cmp(int A,int B){return dfn[A]<dfn[B];}
int k,keypos[N],vis[N],st[M],top,rcd[N],cntrcd;long long g[N][2];
void dp(int u){
g[u][0]=g[u][1]=0;
if(vis[u]){
g[u][0]=2e9;
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];dp(v);
if(E2.w[i]) g[u][1]+=min(g[v][0],1ll+min(g[v][1],g[v][0]));
else g[u][1]+=g[v][0];
}
}
else{
long long de=0;
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];dp(v);
g[u][0]+=g[v][0];
de=max(de,g[v][0]-g[v][1]);
}
g[u][1]=g[u][0]-de;
long long tmp=1;
for(int i=E2.head[u];i;i=E2.last[i]){
int v=E2.to[i];tmp+=min(g[v][1],g[v][0]);
}
g[u][1]=min(g[u][1],tmp);
g[u][0]=min(g[u][0],tmp);
}
return;
}
void solve(){
E2.tot=0;for(int i=1;i<=cntrcd;i++)E2.head[rcd[i]]=0;top=0;cntrcd=0;
k=read();for(int i=1;i<=k;i++)keypos[i]=read(),vis[keypos[i]]=1;
sort(keypos+1,keypos+k+1,cmp);
for(int i=1;i<k;i++)
st[++top]=keypos[i],st[++top]=LCA(keypos[i],keypos[i+1]);
st[++top]=keypos[k];
sort(st+1,st+top+1,cmp);
top=unique(st+1,st+top+1)-st-1;
for(int i=1;i<top;i++){
int tmp=LCA(st[i],st[i+1]);
E2.add(tmp,st[i+1],(dep[tmp]+1!=dep[st[i+1]])),rcd[++cntrcd]=tmp;
}
dp(st[1]);printf("%lld\n",min(g[st[1]][0],g[st[1]][1])>=2e9?-1:min(g[st[1]][0],g[st[1]][1]));
for(int i=1;i<=k;i++)vis[keypos[i]]=0;
return;
}
int main(){
n=read();
for(int i=1;i<n;i++){int u=read(),v=read();E1.add(u,v,0),E1.add(v,u,0);}
dfs(1,1);
for(int i=1;i<=20;i++)
for(int j=1;j<=n;j++)
f[j][i]=f[f[j][i-1]][i-1];
int m=read();for(int i=1;i<=m;i++)solve();
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具