首页 > 其他分享 >2022.11.2 模拟赛题解

2022.11.2 模拟赛题解

时间:2022-11-03 09:17:59浏览次数:55  
标签:一条 int 题解 短路 路径 tre 权值 2022.11 模拟

简要题意

给定一棵 \(n\) 个节点的有根树,树根为 \(1\) 号节点,每个结点有一个权值 \(a_i(|a_i|\leq 10^9)\) ,求包含 \(1\) 的前 \(k\) 小的连通块的权值。

简要题解

前置内容

平面图转对偶图

平面图 :任一两条边不在非顶点除相交。

对偶图 :将平面图的每一个封闭的平面,向与其相邻的的平面连边,在本题树形图的对偶图边权就是子树的权值和。

举个例子:

image

  • 性质: 平面图的最小割等于其对偶图的最短路,最大割最长路

可持久化可并堆求 \(k\) 短路

可持久化可并堆:是一个支持快速合并的堆,合并部分代码如下:

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;
}

具体算法流程如下:

  1. 我们首先从 \(t\) 向 \(s\) 跑一遍最短路,把最短路树建出来,最短路树就是以 \(t\) 为根节点,每个结点到根节点的路径表示着原图上该结点到根节点的一条最短路。
  • 然后我们考虑将最短路上的一些边进行替换或者增加。

此时我们考虑维护一些点对:

\[(a_1,a_2),(a_3,a_4).......(a_{m-1},a_m) \]

表示的是原图上一条路径:

\(s-a_1\) 走最短路树上的路径,经过 \((a_1,a_2)\) 这条边,\(a_2-a_3\) 走最短路树上的路径 ...... 经过 \((a_{m-1},a_m)\) 这条边, \(a_m-t\) 按照最短路树上的路径走。

如下图所示:

image

  1. 我们考虑怎样用一条路径拓展出一条稍微比其劣一点点的路径,我们只考虑改变所维护的最后一条边(因为前面如果要改变就会在之前所枚举)。那么只有两种拓展的情况。
  • 把当前维护的最后一条边给扬了换成一条略大一点的边。
  • 在路径后面新加入一条边。

我们用可持久化可并堆来维护。堆内的每一个节点就表示原图中的一条不在最短路树上的边。原图中每一个点的堆内就维护所有从其及其最短路树上的祖先延伸出去的非树边,一条从 \(x->y\) 权值为 \(e_i\) 的边,定义其新的边权 \(\Delta e=dist[y]-dist[x]+e_i\) ,也就是用这条边替换原最短路上的边造成的最短路长度的变化量。

我们当前维护的最后一条边表示为 $(len,node) $ ,表示这一条路径在这条边之前的路径长度为 \(len\) ,可并堆内 \(node\) 结点所维护的就是当前这一条边。

那么对于第一种情况,就是要查这条边在其所在堆内的后继,直接将左右儿子合并,那么堆顶元素也就是所要找的后继。

对于第二种情况,就是原先 \(a_m-t\) 这段路径是沿着最短路树走,现在我们把其中一段替换为一条非树边 \((a_{m+1},a_{m+2})\) 。那么相当于我们接受当前维护的最后一条边,到达 \(a_m\) ,在 \(a_m\) 的可并堆内找到最小的一条边(也就是堆顶),并变成维护 \((a_{m+1},a_{m+2})\) 这条边。

对于拓展出来的两种情况,把它丢到优先队列里即可,然后就做完了!。

建树的时间复杂度是 \(o(m\log m)\) ,求 \(k\) 短路的复杂度是 \(k\log k\) 。

空间复杂度是 \(o((k+m)\log m)\) 。

关于本题

本题要求前 \(k\) 小的包含 \(1\) 结点的连通块的权值和,那么可以转化为求解一个前 \(k\) 大割,将权值取反就是求前 \(k\) 小割,因此我们把原图转化为对偶图,在最短路上求解前 \(k\) 短路,再加上所有结点的权值和就是第 \(k\) 小的连通块。

关于样例的解释:

image

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;
}

标签:一条,int,题解,短路,路径,tre,权值,2022.11,模拟
From: https://www.cnblogs.com/oscaryangzj/p/16853194.html

相关文章

  • NOIP模拟1
    T1.语言签到题。可以直接\(O(n)\)预处理出来前缀和,但我用了线段树,所以多了一个\(log\)的复杂度。题意转化:找到一个位置为动词,上一个位置为名词,句子末尾是名词,其他地方是......
  • C语言 模拟实现字符串函数 看着一篇够了
    C语言模拟实现字符串操作的库函数求字符串长度strlen思路1.如果碰到\0就代表字符串已经到了末尾size_tmy_strlen(constchar*str){ assert(str!=NULL); //......
  • 11.2 解题报告&CSP-S 2022题解
    T1用时:\(1\)h期望得分:\(70\)~\(80\)实际得分:\(55\)这题考场写了个常数比较小的\(O(n^3)\)的做法,期望得分\(75\)左右,但是由于bfs忘记打标记导致MLE和TLE,挂......
  • 【2022.11.2】Vue基础学习(7)
    内容详细1vue3介绍1.性能的提升打包大小减少41%初次渲染快55%,更新渲染快133%内存减少54%2.源码的升级使用Proxy代替defineProperty实现响应式......
  • ida flare_emu模拟执行批量解密字符串(Orchard_Botnet)
    ......
  • P8671 [蓝桥杯 2018 国 AC] 约瑟夫环 题解
    约瑟夫环有\(\mathcalO(n)\)做法相信大家都知道。这里就不在介绍了,这里给出一个不知道这个结论的\(\mathcalO(n\logn)\)简单做法。考虑直接模拟题意,每次循环往后数......
  • [题解]CF1327F
    首先拆位,然后考虑限制会带来什么。要求与起来是\(1\)的必须全是\(1\),差分打个标记。要求与起来是\(0\)的必须至少有一个\(0\),考虑如何计数。计数问题有可能是动态......
  • CF1729G Cut Substrings 题解
    CF1729GCutSubstrings给出两个字符串\(s,t\),每次可以将字符串\(s\)中任意一个为\(t\)的子串删除,删除位置的字符变为空格(或理解为无实义)。求最少删除几次可以使得......
  • SOJ1669 题解
    题意传送门给定长度为\(N\)的数组\(a\)与\(Q\)个区间,还有一个整数\(M\)。你可以将至多\(M\)个\(a_i\)个变为\(0\),求\[\sum_{i=1}^Q\max_{l_i\lej\ler......
  • NOIP模拟1
    A.语言想到小学英语老师一遍遍地强调:每个句子有且只有一个动词!!忽然给了我灵感。发现不是动词的部分AN可以自由组合,A可以这样连续A(AN),A(A(AN)),唯一不合法的情况就是A在末......