2022.11.2 模拟赛题解
简要题意
给定一棵
简要题解
前置内容
平面图转对偶图
平面图 :任一两条边不在非顶点除相交。
对偶图 :将平面图的每一个封闭的平面,向与其相邻的的平面连边,在本题树形图的对偶图边权就是子树的权值和。
举个例子:
- 性质: 平面图的最小割等于其对偶图的最短路,最大割最长路。
可持久化可并堆求 短路
可持久化可并堆:是一个支持快速合并的堆,合并部分代码如下:
inline int Merge(int x,int y) {
if(!x || !y) return x+y;
if(tre[x].len>tre[y].len) swap(x,y);
int k=++idx; tre[k]=tre[x];
if(rand()&1) tre[k].lc=Merge(tre[k].lc,y);
else tre[k].rc=Merge(tre[k].rc,y);
return k;
}
具体算法流程如下:
- 我们首先从
向 跑一遍最短路,把最短路树建出来,最短路树就是以 为根节点,每个结点到根节点的路径表示着原图上该结点到根节点的一条最短路。
- 然后我们考虑将最短路上的一些边进行替换或者增加。
此时我们考虑维护一些点对:
表示的是原图上一条路径:
如下图所示:
- 我们考虑怎样用一条路径拓展出一条稍微比其劣一点点的路径,我们只考虑改变所维护的最后一条边(因为前面如果要改变就会在之前所枚举)。那么只有两种拓展的情况。
- 把当前维护的最后一条边给扬了换成一条略大一点的边。
- 在路径后面新加入一条边。
我们用可持久化可并堆来维护。堆内的每一个节点就表示原图中的一条不在最短路树上的边。原图中每一个点的堆内就维护所有从其及其最短路树上的祖先延伸出去的非树边,一条从
我们当前维护的最后一条边表示为
那么对于第一种情况,就是要查这条边在其所在堆内的后继,直接将左右儿子合并,那么堆顶元素也就是所要找的后继。
对于第二种情况,就是原先
对于拓展出来的两种情况,把它丢到优先队列里即可,然后就做完了!。
建树的时间复杂度是
空间复杂度是
关于本题
本题要求前
关于样例的解释:
code
点击查看代码
#include<bits/stdc++.h>
#define mem(a) memset(a,0,sizeof(a))
#define LL long long
using namespace std;
const int N=1e5+5;
const LL inf=0x3f3f3f3f3f3f3f3f;
inline int read() {
int x=0,w=0; char ch=getchar();
while(!isdigit(ch)) w|=(ch=='-'), ch=getchar();
while(isdigit(ch)) x=x*10+(ch^48), ch=getchar();
return w?-x:x;
}
int n,k,u,v,m,lf,id[N],fa[N],d[N],leaf[N],a[N];
int hd[N],to[N<<1],ne[N<<1],tc=1;
LL s[N],dist[N],e[N<<4];
int idx,rt[N];
struct Lefttree { int lc,rc,to; LL len; }tre[N<<5];
struct Val {
int node; LL len;
bool friend operator<(Val x,Val y) { return x.len+tre[x.node].len>y.len+tre[y.node].len; }
}now;
priority_queue<Val> Q;
inline void Add(int,int,LL);
inline void Dfs(int);
inline void Col(int,int,int);
inline int Merge(int,int);
int main() {
srand(time(NULL));
n=read(); k=read();
for(int i=1;i<=n;i++) a[i]=read();
for(int i=1;i<n;i++) u=read(), v=read(), Add(u,v,0LL), Add(v,u,0LL);
Dfs(1); tc=1; mem(hd);
Col(1,leaf[1],++m);
for(int i=1;i<lf;i++) Col(leaf[i],leaf[i+1],++m);
Col(leaf[lf],1,++m);
for(int i=1;i<m;i++) Add(i,i+1,0);
for(int x=m-1,eg;x>0;x--) {
dist[x]=inf; eg=1;
for(int i=hd[x],y;i;i=ne[i])
if(dist[y=to[i]]+e[i]<dist[x]) eg=i, dist[x]=dist[y]+e[i];
rt[x]=rt[to[eg]];
for(int i=hd[x],y;i;i=ne[i]) if(i^eg) {
y=to[i];
tre[++idx]=Lefttree{0,0,y,dist[y]-dist[x]+e[i]};
rt[x]=Merge(rt[x],idx);
}
}
printf("%lld\n",dist[1]+s[1]); --k;
now.len=0; now.node=rt[1];
if(now.node) Q.push(now);
int lst;
while(k-- && Q.size()) {
now=Q.top(); Q.pop();
printf("%lld\n",dist[1]+s[1]+now.len+tre[now.node].len);
lst=now.node;
now.node=Merge(tre[lst].lc,tre[lst].rc);
if(now.node) Q.push(now);
now.node=rt[tre[lst].to]; now.len+=tre[lst].len;
if(now.node) Q.push(now);
}
return 0;
}
inline void Add(int x,int y,LL z) {
to[++tc]=y; ne[tc]=hd[x]; hd[x]=tc; e[tc]=z;
}
inline void Dfs(int x) {
bool isleaf=1; s[x]=a[x];
for(int i=hd[x],y;i;i=ne[i]) if((y=to[i])!=fa[x]) {
fa[y]=x; d[y]=d[x]+1; isleaf=0;
Dfs(y);
s[x]+=s[y];
}
if(isleaf) leaf[++lf]=x;
}
inline void Col(int x,int y,int z) {
while(x^y)
if(d[x]<d[y]) id[y]=z, y=fa[y];
else Add(id[x],z,-s[x]), x=fa[x];
}
inline int Merge(int x,int y) {
if(!x || !y) return x+y;
if(tre[x].len>tre[y].len) swap(x,y);
int k=++idx; tre[k]=tre[x];
if(rand()&1) tre[k].lc=Merge(tre[k].lc,y);
else tre[k].rc=Merge(tre[k].rc,y);
return k;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】