@description@

定义函数 f(G, x, y) 为 G 中点 x 和点 y 之间的所有路径的权重(该路径上各边权的最大值)的最小值,其中 G 为一个有边权的无向连通图。

给定两个 N 个点 M 条边连通图 G1 和 G2。请你计算:

\[S = \sum_{i=1}^{N-1}\sum_{j=i+1}^{B}f(G1, i, j)*f(G2, i, j) \mod 998244353
\]

输入格式

输入的第一行包含两个整数 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,则问题变为:

\[S = \sum_{i=1}^{N-1}\sum_{j=i+1}^{N}T1.key(T2.lca(i, j))*T2.key(T2.lca(i, j))
\]

求两棵树 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的更多相关文章

  1. leetcode[164] Maximum Gap

    梅西刚梅开二度,我也记一题. 在一个没排序的数组里,找出排序后的相邻数字的最大差值. 要求用线性时间和空间. 如果用nlgn的话,直接排序然后判断就可以了.so easy class Solution ...

  2. [Swift]LeetCode152. 乘积最大子序列 | Maximum Product Subarray

    Given an integer array nums, find the contiguous subarray within an array (containing at least one n ...

  3. [LeetCode] 111. Minimum Depth of Binary Tree ☆(二叉树的最小深度)

    [Leetcode] Maximum and Minimum Depth of Binary Tree 二叉树的最小最大深度 (最小有3种解法) 描述 解析 递归深度优先搜索 当求最大深度时,我们只要 ...

  4. 【leetcode 桶排序】Maximum Gap

    1.题目 Given an unsorted array, find the maximum difference between the successive elements in its sor ...

  5. [LeetCode]152. Maximum Product Subarray

    This a task that asks u to compute the maximum product from a continue subarray. However, you need t ...

  6. LeetCode 164. Maximum Gap[翻译]

    164. Maximum Gap 164. 最大间隔 Given an unsorted array, find the maximum difference between the successi ...

  7. HTML5游戏源码 飞翔的字母 可自定义内容

    相信大家都玩过飞翔的小鸟吧,当然,可能已经有很多人因为这个游戏砸了不少手机.吼吼. 废话不多说,回到主题,源码如下. 博客园上传空间大小有限制,没法上传了,需要打包源码的朋友们请留言邮箱地址.当然还有 ...

  8. ASP.NET MVC5+EF6+EasyUI 后台管理系统(33)-MVC 表单验证

    系列目录 注:本节阅读需要有MVC 自定义验证的基础,否则比较吃力 一直以来表单的验证都是不可或缺的,微软的东西还是做得比较人性化的,从webform到MVC,都做到了双向验证 单单的用js实现的前端 ...

  9. XE2:查看Extended Events收集的数据

    SQL Server 使用Target来存储Events,Target 能够将Events存储到File中(扩展名是 xel),或 memoy buffer 中(Ring Buffer),Event ...

随机推荐

  1. nfs网络共享服务基础

    nfs原理 1.开启RPC服务 2.NFS向RPC服务注册启动的端口 3.用户向RPC询问NFS服务的端口 4.RPC返回端口给客户端 5.客户端通过获得的端口与NFS服务器进行数据传输 实验步骤 一 ...

  2. centos 安装nginx + 多个tomcat负载均衡

    今天在centos上安装了两个tomcat和nginx,进行配置.今天记录的只是最基本的实现测试.(不包含使用redis进行session共享) Nginx 是一款轻量级的Web 服务器/反向代理服务 ...

  3. python实例 字符串

    比起C/C++,Python处理字符串的方式实在太让人感动了.把字符串当列表来用吧. #! /usr/bin/python word="abcdefg" a=word[2] pri ...

  4. Flask – SQLite:增加成员

    目录 简介 结构 展示 技术 运行 代码 创建数据库表单 views视图 models模块 home主页 list列表页 result消息结果页 studentst添加成员 简介 结构 $ tree ...

  5. web前端学习(三)css学习笔记部分(8)-- SVN的介绍和应用、CSS动画效果、CSS3布局属性全接触

    15.SVN的介绍和应用 15.1.SVN的介绍和应用课程概要 将代码进行集中管理,有版本号的进行迭代,方便集体工作的build流程 15.2.SVN的介绍 SVN是Subversion的简称,是一个 ...

  6. 常用命令4-文件搜索命令 2- whereis

    whereis比较局限,只能查看系统命令.

  7. 2017年Android SDK下载安装及配置教程(附带原文地址)

    首先声明: Unity版本5.6.3f1 最近试着在Unity中利用高通做AR开发时,发布项目文件需要发布到Android平台,遇到一些问题,看了网上的一些资料,踩了一些坑,现在总结出来,希望有相同的 ...

  8. PHP-xdebug+PHPStorm的debug安装(未完)

    PHP的xdebug安装 一.首先去phpinfo()查看自己的php是32还是64位的,再根据php版本在下面的网站https://xdebug.org/download.php选择xdebug插件 ...

  9. LintCode_111 爬楼梯

    题目 假设你正在爬楼梯,需要n步你才能到达顶部.但每次你只能爬一步或者两步,你能有多少种不同的方法爬到楼顶部? 比如n=3,中不同的方法 返回 3 1 2 3 5 8 13... step[2] = ...

  10. Beetl 3中文文档 转载 http://ibeetl.com/guide/

    Beetl作者:李家智(闲大赋) <xiandafu@126.com> 1. 什么是Beetl 广告:闲大赋知识星球,付费会员 Beetl( 发音同Beetle ) 目前版本是3.0.7, ...