[SDOI2011]消耗战
做题时间:2022.7.7
有一棵 个点、根为 的有根树,每一条边有一个边权 ,现有 次询问,第 次询问给出 个关键点,现在删除一些边,使得 个关键点都与根不联通,问删除的边的最小边权和(询问之间无关联)。
第一行一个整数表示
接下来 行每行两个整数 表示树上的每一条边
接下来一行一个整数 表示询问次数
接下来 行每行第一个数为 ,接下来 个数表示本次询问的关键点
共 行每行一个整数表示答案。
虚树、树形DP
不考虑数据规模很容易想到树形DP。
观察数据后发现有多次询问,且 , 若能在Dp中尽量不遍历非关键节点,那么复杂度就接近
像这种DP过程中大量访问非关键节点(对关键节点没有影响),且关键节点数较小的题目,一般都用虚树+DP解决,即重新建立一棵仅包含关键节点及其LCA的树再DP
值得注意的是虚树的边权怎么订。首先可以知道虚树上两点的边权即为在原树中两点的距离最小值,这需要用倍增DP维护且复杂度贼大 ,但这道题目对于一个点而言,切除其与根的联系,必定切除的是其本身与根路径上边权最小的那一条边,因此直接记录虚树上每一个点与根节点路径上边权最小值即可。
接下来就是简单的DP了,定义 表示以点 为根的子树的答案,转移方程:
表示的是 到根的最小边权
另外要注意的是,如果原图退化成菊花图,那么答案就可能爆int,因此dp数组要开long long,初始值也应当调大。
#include<cstdio> #include<iomanip> #include<algorithm> using namespace std; const int N=2e5+5e4+50,M=5e5+50; const long long INF=0x3f3f3f3f3f3f3f3f; typedef long long ll; struct edge{ int to,val,nxt; }a[N*10]; int head[N],dep[N],f[N][30],n,cnt,tot,top; int id[N],st[N],h[M],k,m; ll dp[N],Minw[N]; bool Is_h[N]; bool cmp(int a,int b){return id[a]<id[b];} inline ll Min(ll a,ll b){return a<b?a:b;} int Read() { int x=0; char ch=getchar(); while(ch>'9'||ch<'0') ch=getchar(); while(ch>='0'&&ch<='9'){ x=(x<<1)+(x<<3)+ch-48; ch=getchar(); } return x; } void add(int u,int v,int w) { cnt++; a[cnt].to=v; a[cnt].val=w; a[cnt].nxt=head[u]; head[u]=cnt; } void DFS(int u,int fa) { id[u]=++tot; dep[u]=dep[fa]+1; f[u][0]=fa; for(int i=1;i<=20;i++) f[u][i]=f[f[u][i-1]][i-1]; for(int i=head[u];i;i=a[i].nxt){ int v=a[i].to; if(v!=fa) Minw[v]=Min(Minw[u],a[i].val),DFS(v,u); } } void Swap(int &x,int &y){int t=x;x=y;y=t;} int LCA(int x,int y) { if(dep[x]<dep[y]) Swap(x,y); for(int i=20;i>=0;i--){ if(dep[f[x][i]]>=dep[y]) x=f[x][i]; } if(x==y) return x; for(int i=20;i>=0;i--){ if(f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i]; } return f[x][0]; } void Work()//虚树的核心代码 { sort(h+1,h+1+k,cmp); st[top=1]=1,head[1]=0; for(int i=1;i<=k;i++){ if(h[i]!=1){ int l=LCA(h[i],st[top]); //lca分四种情况:在st[top];在st[top]与st[top-1]之间;在st[top-1];在st[top-1]上方 if(id[l]!=id[st[top]]){ while(id[l]<id[st[top-1]]){ add(st[top],st[top-1],0); add(st[top-1],st[top],0); top--; } if(id[l]>id[st[top-1]]){ head[l]=0,add(l,st[top],0); add(st[top],l,0); st[top]=l; } else{ add(l,st[top],0); add(st[top],l,0); top--; } } } head[h[i]]=0,st[++top]=h[i]; } //最后一条链 for(int i=1;i<top;i++){ add(st[i],st[i+1],0); add(st[i+1],st[i],0); } top=0; } void DP(int u,int fa) { dp[u]=0; for(int i=head[u];i;i=a[i].nxt){ int v=a[i].to; if(v!=fa){ DP(v,u); if(Is_h[v]) dp[u]+=Minw[v]; else dp[u]+=Min(dp[v],Minw[v]); } } } int main() { n=Read(); for(int i=1;i<=n-1;i++){ int u=Read(),v=Read(),w=Read(); add(u,v,w),add(v,u,w); } Minw[1]=INF; DFS(1,0); m=Read(); while(m--){ scanf("%d",&k); for(int i=1;i<=k;i++){ scanf("%d",&h[i]); Is_h[h[i]]=true; } Work(); DP(1,0); for(int i=1;i<=k;i++) Is_h[h[i]]=false; printf("%lld\n",dp[1]); } return 0; }
本文作者:lxzy
本文链接:https://www.cnblogs.com/Unlimited-Chan/p/16456934.html
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步