@codechef - MXMN@ Maximum and Minimum
@description@
定义函数 f(G, x, y) 为 G 中点 x 和点 y 之间的所有路径的权重(该路径上各边权的最大值)的最小值,其中 G 为一个有边权的无向连通图。
给定两个 N 个点 M 条边连通图 G1 和 G2。请你计算:
\]
输入格式
输入的第一行包含两个整数 N 和 M。
接下来的 M 行每行包含三个整数 u,v 和 w,表示 G1 中的点 u 和 v 由一条权重为 w 的边连接。
再接下来的 M 行每行包含三个整数 u,v 和 w,表示 G2 中的点 u 和 v 由一条权重为 w 的边连接。
输出格式
对于每组数据,输出一行包含一个整数,表示 S 对 998244353 取模的结果。
**数据范围 **
• 1 ≤ N ≤ 10^5
• M = 2N
• 1 ≤ u,v ≤ N
• 1 ≤ w ≤ 10^8
• G1 和 G2 都是连通图
样例数据
输入
3 6
1 2 3
2 3 1
3 1 2
1 2 4
2 3 5
3 1 6
1 2 2
2 3 1
3 1 3
1 2 5
2 3 4
3 1 6
输出
9
@solution@
考虑先分别建出两个图 G1、G2 的 kruskal 重构树 T1、T2,则问题变为:
\]
求两棵树 lca 的权值乘积的和实际上是边分树合并的经典套路。
我们考虑一遍边分治(因为 kruskal 重构树本身是二叉树,所以不用重构)建出边分树,左儿子存储中心边深度较小的那边连通块,右儿子存储中心边深度较大的那边连通块。
于是跨越中心边 (m1, m2) 的路径 (u, v) 的 lca 只跟深度较小的那块连通块有关,不妨记 u 是深度较小的,则 lca(u, v) = lca(u, m1)。不妨将 u 对应的 T1.key(lca(u, m1)) 存储下来记作 f[u]。
枚举 T2.lca(i, j) 为 p 算出对应的 T1.key 之和。考虑将 p 的左右儿子的边分树合并得到 p 的边分树,同时统计答案。
考虑在边分树上维护深度较小那边所有点 f 之和 sum,维护深度较大那边点的数量 cnt。则合并时用两棵边分树的 sum 和 cnt 两两相乘求和就是我们想要得到的东西。
@accepted code@
#include<cstdio>
#include<algorithm>
using namespace std;
const int MAXN = 200000;
const int MOD = 998244353;
int lg[2*MAXN + 5];
struct Graph{
	struct edge{
		int to; bool tag;
		edge *nxt, *rev;
	}edges[2*MAXN + 5], *adj[MAXN + 5], *ecnt;
	Graph() {ecnt = &edges[0];}
	void addedge(int u, int v) {
		edge *p = (++ecnt), *q = (++ecnt);
		p->to = v, p->nxt = adj[u], adj[u] = p, p->tag = false;
		q->to = u, q->nxt = adj[v], adj[v] = q, q->tag = false;
		p->rev = q, q->rev = p;
	}
	int dep[MAXN + 5], dfn[2*MAXN + 5], fir[MAXN + 5], dcnt;
	void dfs(int x, int f) {
		dep[x] = dep[f] + 1, dfn[++dcnt] = x, fir[x] = dcnt;
		for(edge *p=adj[x];p;p=p->nxt) {
			if( p->to == f ) continue;
			dfs(p->to, x), dfn[++dcnt] = x;
		}
	}
	int st[20][2*MAXN + 5];
	void get_st() {
		for(int i=1;i<=dcnt;i++)
			st[0][i] = dfn[i];
		for(int j=1;j<20;j++) {
			int t = 1<<(j-1);
			for(int i=1;i+t<=dcnt;i++)
				st[j][i] = (dep[st[j-1][i]] < dep[st[j-1][i+t]]) ? st[j-1][i] : st[j-1][i+t];
		}
	}
	void build(int x) {dcnt = 0; dfs(x, 0); get_st();}
	int lca(int x, int y) {
		if( fir[x] > fir[y] ) swap(x, y);
		x = fir[x], y = fir[y];
		int k = lg[y-x+1], l = (1<<k);
		return (dep[st[k][x]] < dep[st[k][y-l+1]]) ? st[k][x] : st[k][y-l+1];
	}
}G1, G2;
int siz[MAXN + 5];
bool cmp(Graph::edge *a, Graph::edge *b, int tot) {
	if( a == NULL ) return false;
	if( b == NULL ) return true;
	return max(siz[a->to], tot-siz[a->to]) < max(siz[b->to], tot-siz[b->to]);
}
Graph::edge *get_mid(int x, int f, int tot) {
	Graph::edge *ret = NULL; siz[x] = 1;
	for(Graph::edge *p=G1.adj[x];p;p=p->nxt) {
		if( p->tag || p->to == f ) continue;
		Graph::edge *tmp = get_mid(p->to, x, tot);
		siz[x] += siz[p->to];
		if( cmp(tmp, ret, tot) ) ret = tmp;
		if( cmp(p, ret, tot) ) ret = p;
	}
	return ret;
}
int ch[2][MAXN + 5], etot = 0;
bool dir[32][MAXN + 5]; int key[32][MAXN + 5];
int a[MAXN + 5], b[MAXN + 5], N, M;
void dfs(const int &k, int x, int f, bool t, const int &dep) {
	dir[dep][x] = t;
	if( !t ) key[dep][x] = a[G1.lca(k, x)];
	for(Graph::edge *p=G1.adj[x];p;p=p->nxt) {
		if( p->tag || p->to == f ) continue;
		dfs(k, p->to, x, t, dep);
	}
}
int divide(int x, int tot, int dep) {
	Graph::edge *m = get_mid(x, 0, tot);
	if( m == NULL ) return -1;
	m->tag = m->rev->tag = true;
	int tmp = (++etot);
	dfs(m->to, m->to, 0, G1.dep[m->to]>G1.dep[m->rev->to], dep);
	dfs(m->rev->to, m->rev->to, 0, G1.dep[m->to]<G1.dep[m->rev->to], dep);
	ch[G1.dep[m->to]>G1.dep[m->rev->to]][tmp] = divide(m->to, siz[m->to], dep + 1);
	ch[G1.dep[m->to]<G1.dep[m->rev->to]][tmp] = divide(m->rev->to, tot-siz[m->to], dep + 1);
	return tmp;
}
struct edge{
	int u, v, w;
	friend bool operator < (edge a, edge b) {
		return a.w < b.w;
	}
}e[MAXN + 5];
int fa[MAXN + 5];
int find(int x) {
	return fa[x] = (fa[x] == x) ? x : find(fa[x]) ;
}
struct node{
	node *ch[2];
	int cnt, sum;
}nd[32*MAXN + 5], *rt[MAXN + 5], *ncnt, *NIL;
node *new_tree(int nw, int x, int dep) {
	if( nw == -1 ) return NIL;
	node *p = (++ncnt);
	if( !dir[dep][x] ) p->sum = (p->sum + key[dep][x])%MOD;
	else p->cnt = (p->cnt + 1)%MOD;
	p->ch[dir[dep][x]] = new_tree(ch[dir[dep][x]][nw], x, dep + 1);
	p->ch[!dir[dep][x]] = NIL;
	return p;
}
int ans = 0, res = 0;
node *merge(node *rt1, node *rt2) {
	if( rt1 == NIL ) return rt2;
	if( rt2 == NIL ) return rt1;
	res = (res + 1LL*rt1->cnt*rt2->sum%MOD) % MOD;
	res = (res + 1LL*rt1->sum*rt2->cnt%MOD) % MOD;
	rt1->sum = (rt1->sum + rt2->sum)%MOD;
	rt1->cnt = (rt1->cnt + rt2->cnt)%MOD;
	rt1->ch[0] = merge(rt1->ch[0], rt2->ch[0]);
	rt1->ch[1] = merge(rt1->ch[1], rt2->ch[1]);
	return rt1;
}
void dfs2(int x, int f) {
	if( x <= N )
		rt[x] = new_tree(1, x, 0);
	else rt[x] = NIL;
	for(Graph::edge *p=G2.adj[x];p;p=p->nxt) {
		if( p->to == f ) continue;
		dfs2(p->to, x);
	}
	res = 0;
	for(Graph::edge *p=G2.adj[x];p;p=p->nxt) {
		if( p->to == f ) continue;
		rt[x] = merge(rt[x], rt[p->to]);
	}
	ans = (ans + 1LL*res*b[x]%MOD)%MOD;
}
void init() {
	for(int i=2;i<=2*MAXN;i++)
		lg[i] = lg[i>>1] + 1;
	ncnt = NIL = &nd[0];
	NIL->ch[0] = NIL->ch[1] = NIL;
	NIL->cnt = NIL->sum = 0;
}
int main() {
	init();
	scanf("%d%d", &N, &M);
	for(int i=1;i<=M;i++)
		scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
	sort(e + 1, e + M + 1);
	for(int i=1;i<=N;i++)
		fa[i] = i;
	int cnt = N;
	for(int i=1;i<=M;i++) {
		int fu = find(e[i].u), fv = find(e[i].v);
		if( fu != fv ) {
			a[++cnt] = e[i].w; fa[cnt] = cnt;
			fa[fu] = fa[fv] = cnt;
			G1.addedge(cnt, fu), G1.addedge(cnt, fv);
		}
	}
	G1.build(find(1)); divide(1, cnt, 0);
	for(int i=1;i<=M;i++)
		scanf("%d%d%d", &e[i].u, &e[i].v, &e[i].w);
	sort(e + 1, e + M + 1);
	for(int i=1;i<=N;i++)
		fa[i] = i;
	cnt = N;
	for(int i=1;i<=M;i++) {
		int fu = find(e[i].u), fv = find(e[i].v);
		if( fu != fv ) {
			b[++cnt] = e[i].w;
			fa[fu] = fa[fv] = fa[cnt] = cnt;
			G2.addedge(cnt, fu), G2.addedge(cnt, fv);
		}
	}
	dfs2(find(1), 0);
	printf("%d\n", ans);
}
@details@
求 lca 时写了个 st 表求 rmq,然而 st 表需要二倍长度,然而我用的是一倍长度。。。
@codechef - MXMN@ Maximum and Minimum的更多相关文章
- leetcode[164] Maximum Gap
		梅西刚梅开二度,我也记一题. 在一个没排序的数组里,找出排序后的相邻数字的最大差值. 要求用线性时间和空间. 如果用nlgn的话,直接排序然后判断就可以了.so easy class Solution ... 
- [Swift]LeetCode152. 乘积最大子序列 | Maximum Product Subarray
		Given an integer array nums, find the contiguous subarray within an array (containing at least one n ... 
- [LeetCode] 111. Minimum Depth of Binary Tree ☆(二叉树的最小深度)
		[Leetcode] Maximum and Minimum Depth of Binary Tree 二叉树的最小最大深度 (最小有3种解法) 描述 解析 递归深度优先搜索 当求最大深度时,我们只要 ... 
- 【leetcode 桶排序】Maximum Gap
		1.题目 Given an unsorted array, find the maximum difference between the successive elements in its sor ... 
- [LeetCode]152. Maximum Product Subarray
		This a task that asks u to compute the maximum product from a continue subarray. However, you need t ... 
- LeetCode 164. Maximum Gap[翻译]
		164. Maximum Gap 164. 最大间隔 Given an unsorted array, find the maximum difference between the successi ... 
- HTML5游戏源码 飞翔的字母 可自定义内容
		相信大家都玩过飞翔的小鸟吧,当然,可能已经有很多人因为这个游戏砸了不少手机.吼吼. 废话不多说,回到主题,源码如下. 博客园上传空间大小有限制,没法上传了,需要打包源码的朋友们请留言邮箱地址.当然还有 ... 
- ASP.NET MVC5+EF6+EasyUI 后台管理系统(33)-MVC 表单验证
		系列目录 注:本节阅读需要有MVC 自定义验证的基础,否则比较吃力 一直以来表单的验证都是不可或缺的,微软的东西还是做得比较人性化的,从webform到MVC,都做到了双向验证 单单的用js实现的前端 ... 
- XE2:查看Extended Events收集的数据
		SQL Server 使用Target来存储Events,Target 能够将Events存储到File中(扩展名是 xel),或 memoy buffer 中(Ring Buffer),Event ... 
随机推荐
- 玩转 Django2.0 笔记1
			模板静态 路由 urls.py urlpatterns = [ path("<year>/<int:month>/<slug:day>",my ... 
- Python之路,Day2 - Python基础(转载Alex)
			Day2-转自金角大王 本节内容 列表.元组操作 字符串操作 字典操作 集合操作 文件操作 字符编码与转码 1. 列表.元组操作 列表是我们最以后最常用的数据类型之一,通过列表可以对数据实现最方便的存 ... 
- 关于neo4j的嵌入式和驱动包模式该如何选择,还请解惑
			看了网上的一些资料和Neo4j权威指南这本书.与图遍历相关的介绍都是基于嵌入式模式下的java Api.但是个人觉得在实际的项目中,嵌入式的模式,代码必须放在数据库所在服务器上,且服务器的启停操作都在 ... 
- 计蒜客 Flashing Fluorescents(状压DP)
			You have nn lights, each with its own button, in a line. Pressing a light’s button will toggle that ... 
- 【weex】h5weex-example
			这个就是一个练手的基础性的demo,不过也是有很多值得学习的东西的 效果如下 项目地址为:https://github.com/h5weex/h5weex-example 可能是我找到的项目比较少,很 ... 
- django中静态资源
			创建静态资源存放路径,为了设置静态媒体,你需要设立存储它们的目录.在你的项目目录(例如/myproject/),创建叫做static的目录.在static里再创建一个images目录和js目录 设置项 ... 
- new操作符实现过程
			var obj = new Object(); //创建新对象 一. new是干嘛的? new操作符用来生成一个新的对象, 它后面必须跟上一个函数(否则, 会抛出TypeError异常), 这个函数就 ... 
- ubuntu 安装 lrzsz 上传下载
			原文:ubuntu 安装 lrzsz 上传下载 版权声明:本文为博主原创文章,随意转载. https://blog.csdn.net/Michel4Liu/article/details/808223 ... 
- elipse egit的使用
- element-ui表格列金额显示两位小数
			对于金额的显示,大多情况下需要保留两位小数,比如下面的(表格采用 element-ui): 在vue.js中,对文本的处理通常是通过设置一系列的过滤器,过滤器可以用在两个地方:双花括号插值 和 v-b ... 
