IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 数据结构与算法 -> [ACNOI2022]《普林斯普的荣光》 -> 正文阅读

[数据结构与算法][ACNOI2022]《普林斯普的荣光》

题目

题目背景
多年以后,我在搬砖的工地上,远远望见了一个身影:仍然是如同漆黑的杆子一般,手丝毫不摆动,而脚一步一印往前挪动,像吸盘,像祂正试图学会在地上平移,用自己的脚。

我几乎要失声叫出祂的名字;这个名字我花了十年去恐惧,花了十年去遗忘,但我看到祂的一瞬间,所有努力都是徒劳,所有意义都被抹杀。

任何事物都逃不过祂的眼睛。当我从惊慌中挣脱出来,抬头便遇上祂的审视。祂胸前的金牌扰乱着我的神志。我只能勉强从牙缝中挤出来几个字:

普……普林斯普!”

祂的嘴唇没有动,我的脑海中却响起一句话。这真的是我刚刚所顿悟的么?

校长笑话,烟雾弹也;水群摆烂,麻痹对手尔。”

回忆如波涛翻滚。仿佛有无数个祂同时在我耳边低语:

你看,你又被骗了吧。”

题目描述
给出一个无向连通图 G = ( V , E ) G=(V,E) G=(V,E),定义新图 G ′ = ( V , E ′ ) G'=(V,E') G=(V,E) 满足 E ′ = { ? a , b , c a ? ∣ dis ( a , b ) ? d a } E'=\{\langle a,b,c_a\rangle\mid\text{dis}(a,b)\leqslant d_a\} E={?a,b,ca??dis(a,b)?da?},其中 ? a , b , c ? \langle a,b,c\rangle ?a,b,c? 表示边权为 c c c a → b a\to b ab 有向边,而 dis ( a , b ) \text{dis}(a,b) dis(a,b) G G G a , b a,b a,b 最短路的边数。

求新图 G ′ G' G 1 1 1 号点到每个点的最短路。

数据范围与约定
n ? 2 × 1 0 5 ∧ d i ∈ [ 1 , n ] ∧ c i ∈ [ 1 , 1 0 9 ] ∧ ∣ E ∣ ? ∣ V ∣ + 50 n\leqslant 2\times 10^5\land d_i\in[1,n]\land c_i\in[1,10^9]\land |E|\leqslant |V|+50 n?2×105di?[1,n]ci?[1,109]E?V+50 。时限 4 s \rm 4s 4s

思路

注意到 ∣ E ∣ ? ∣ V ∣ + 50 |E|\leqslant|V|+50 E?V+50,考虑建一棵生成树,然后讨论非树边的影响。必要条件是搞懂树边的影响。即,先考虑原图是一棵树的情况。

树上路径问题?类似此题,可以直接点分治优化建图。不需要边分治,因为 dis ( y , z ) ? d x ? dis ( x , z ) \text{dis}(y,z)\leqslant d_x-\text{dis}(x,z) dis(y,z)?dx??dis(x,z) x → y x\to y xy 有边的充分条件,即 dis ( x , y ) \text{dis}(x,y) dis(x,y) 可以实际上不经过 z z z,不需要边分治来保障 “必须经过” 的要求。

当然,现在想想,优化建图跑最短路,不如 O U Y E \sf OUYE OUYE 所说 数据结构优化转移。建出显式图,只会增加不必要的时空复杂度。非最短路问题,倒是有可能让代码实现更简单(比如 2-sat \text{2-sat} 2-sat 问题)。

非树边呢?相当于转移过程中,需要走过某条非树边。点分治的本质是,考虑 dis \text{dis} dis 必须经过某个点;将每条非树边的任一端点设为 “特殊点”,对经过特殊点的 dis \text{dis} dis 进行转移即可。

显式建图,似乎有 O ( n log ? n + n k ) \mathcal O(n\log n+nk) O(nlogn+nk) 条边,其中 k = ∣ E ∣ ? ∣ V ∣ + 1 k=|E|-|V|+1 k=E?V+1 。跑 d i j k s t r a \tt dijkstra dijkstra 岂不是 O [ n log ? n log ? ( n log ? n ) ] \mathcal O[n\log n\log(n\log n)] O[nlognlog(nlogn)] 了吗?可以类比此题,将 ( v i + c i ) (v_i+c_i) (vi?+ci?) 放入 h e a p \tt heap heap 中;显式建图,则其等价于将边权为 0 0 0 的连通块进行 b f s \rm bfs bfs 更新。这样可以保证每个点直接得到答案,复杂度 O ( n log ? n + n k ) \mathcal O(n\log n+nk) O(nlogn+nk)

代码

#include <cstdio> // JZM yydJUNK!!!
#include <iostream> // XJX yyds!!!
#include <algorithm> // XYX yydLONELY!!!
#include <cstring> // (the STRONG LONG for loneliness)
#include <cctype> // DDG yydDOG & ZXY yydBUS !!!
#include <queue>
# define rep(i,a,b) for(int i=(a); i<=(b); ++i)
# define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
# define drep(i,a,b) for(int i=(a); i>=(b); --i)
typedef long long llong;
inline int readint(){
	int a = 0, c = getchar(), f = 1;
	for(; !isdigit(c); c=getchar())
		if(c == '-') f = -f;
	for(; isdigit(c); c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}

const int MAXN = 200005, MAXM = 200055;
# define _go(i,x) for(int i=head[x]; ~i; i=e[i].nxt)
struct Edge{ int to, nxt; };
Edge e[MAXN<<1]; int head[MAXN], cntEdge;
void addEdge(int a, int b){
	e[cntEdge].to = b, e[cntEdge].nxt = head[a];
	head[a] = cntEdge ++;
}
const int LOGN = 18;
int siz[MAXN], dep[LOGN][MAXN], fa[MAXN][LOGN];
bool vis[MAXN]; int whole, core;
void findRoot(int x, int pre){
	siz[x] = 1; int mx = 0;
	_go(i,x) if((i^1) != pre && !vis[e[i].to]){
		findRoot(e[i].to,i), siz[x] += siz[e[i].to];
		mx = std::max(mx,siz[e[i].to]);
	}
	if((mx<<1) <= whole && whole <= (siz[x]<<1)) core = x;
}
int *group[MAXN];
int* collect(const int &level, int x, int pre, int* ptr){
	fa[x][level] = core, *(ptr ++) = x;
	_go(i,x) if((i^1) != pre && !vis[e[i].to]){
		dep[level][e[i].to] = dep[level][x]+1;
		ptr = collect(level,e[i].to,i,ptr);
	}
	return ptr;
}
void solve(int x, int level){
	static int buc[MAXN], tmp[MAXN]; ///< bucket sort
	findRoot(x,-1), memset(buc,0,whole<<2);
	dep[level][core] = 0, collect(level,core,-1,tmp);
	rep0(i,0,whole) ++ buc[dep[level][tmp[i]]];
	rep0(i,1,whole) buc[i] += buc[i-1];
	group[core] = new int[whole+1];
	*group[core] = core, group[core][whole] = -1;
	for(int *i=tmp+1; i!=tmp+whole; ++i){
		int &rnk = buc[dep[level][*i]-1];
		group[core][rnk ++] = *i;
	}
	vis[core] = true;
	const int _core = core;
	_go(i,core) if(!vis[e[i].to]){
		if(siz[e[i].to] > siz[_core])
			whole = siz[x]-siz[_core];
		else whole = siz[e[i].to];
		solve(e[i].to,level+1);
	}
}

void bfs(int *que, int *dis, int x, const int &n){
	memset(dis+1,-1,n<<2), dis[x] = 0;
	int *bac = que; *bac = x;
	for(; que!=bac+1; ++que) // keep releasing
		_go(i,x=*que) if(!(~dis[e[i].to])){
			dis[e[i].to] = dis[x]+1;
			++ bac, *bac = e[i].to;
		}
}

namespace UFS{
	int fa[MAXN];
	int find(int a);
	bool merge(int a, int b);
}
bool bad[MAXN]; int tot;
const int MAXK = 102;
int dis[MAXK][MAXN], que[MAXK][MAXN];
int *que_ptr[MAXK];

typedef std::pair<llong,int> PII;
llong ans[MAXN]; extern bool vis[MAXN];
std::priority_queue<PII> pq;
int radius[MAXN], cost[MAXN];
int main(){
	int n = readint(), m = readint();
	rep(i,1,n) UFS::fa[i] = i;
	memset(head+1,-1,n<<2);
	std::queue< std::pair<int,int> > bad_edge;
	for(int i=1,a,b; i<=m; ++i){
		a = readint(), b = readint();
		if(UFS::merge(a,b))
			addEdge(a,b), addEdge(b,a);
		else{
			bad[a] = bad[b] = true;
			bad_edge.push(std::make_pair(a,b));
		}
	}
	whole = n, solve(1,0); memset(vis+1,false,n);
	for(; !bad_edge.empty(); bad_edge.pop()){
		const std::pair<int,int> &p = bad_edge.front();
		addEdge(p.first,p.second), addEdge(p.second,p.first);
	}
	rep(i,1,n) if(bad[i]) bfs(que[tot],dis[tot],i,n), ++ tot;
	rep0(i,0,tot) que_ptr[i] = que[i];
	rep(i,1,n){
		radius[i] = readint();
		cost[i] = -readint(); // negated
	}
	ans[1] = 0, vis[1] = true;
	pq.push(PII{cost[1],1});
	while(!pq.empty()){
		int x = pq.top().second; pq.pop();
		rep0(j,0,LOGN) if(fa[x][j]) // exist
			for(int* &i=group[fa[x][j]]; ~(*i); ++i){
				if(dep[j][*i]+dep[j][x] > radius[x]) break;
				if(vis[*i]) continue; // updated
				ans[*i] = ans[x]+cost[x], vis[*i] = true;
				pq.push(PII{ans[*i]+cost[*i],*i});
			}
		rep0(j,0,tot) for(int* &i=que_ptr[j]; i!=que[j]+n; ++i){
			if(dis[j][*i]+dis[j][x] > radius[x]) break;
			if(vis[*i]) continue; // updated
			ans[*i] = ans[x]+cost[x], vis[*i] = true;
			pq.push(PII{ans[*i]+cost[*i],*i});
		}
	}
	rep(i,2,n) printf("%lld\n",-ans[i]);
	return 0;
}

int UFS::find(int a){
	if(a == fa[a]) return a;
	return fa[a] = find(fa[a]);
}
bool UFS::merge(int a, int b){
	if(find(a) == find(b)) return false;
	fa[fa[a]] = fa[b]; return true;
}
  数据结构与算法 最新文章
【力扣106】 从中序与后续遍历序列构造二叉
leetcode 322 零钱兑换
哈希的应用:海量数据处理
动态规划|最短Hamilton路径
华为机试_HJ41 称砝码【中等】【menset】【
【C与数据结构】——寒假提高每日练习Day1
基础算法——堆排序
2023王道数据结构线性表--单链表课后习题部
LeetCode 之 反转链表的一部分
【题解】lintcode必刷50题<有效的括号序列
上一篇文章      下一篇文章      查看所有文章
加:2022-03-15 22:50:27  更:2022-03-15 22:55:08 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2025年1日历 -2025/1/9 17:15:18-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码