Solution -「UOJ #87」mx 的仙人掌
\(\mathcal{Description}\)
Link.
给出含 \(n\) 个结点 \(m\) 条边的仙人掌图。\(q\) 次询问,每次询问给出一个点集 \(S\),求 \(S\) 内两两结点最短距离的最大值。
\(n,\sum|S|\le3\times10^5\)。
\(\mathcal{Solution}\)
圆方树 + 虚树 = 虚圆方树!
首先,考虑对于整个仙人掌怎么求答案:建出圆方树,DP 记录子树最深结点深度,在方点处单调队列合并圆儿子的两条链贡献答案即可。
接下来,只需要把“虚圆方树”给弄出来就好。关键即在于满足方点周围一定裹着它自己管辖的圆点的性质,那么在建虚树边时特殊考虑一下就完成啦。
\(\mathcal{Code}\)
/* Clearink */
#include <cstdio>
#include <vector>
#define rep( i, l, r ) for ( int i = l, repEnd##i = r; i <= repEnd##i; ++i )
#define per( i, r, l ) for ( int i = r, repEnd##i = l; i >= repEnd##i; --i )
inline char fgc() {
static char buf[1 << 17], *p = buf, *q = buf;
return p == q && ( q = buf + fread( p = buf, 1, 1 << 17, stdin ), p == q )
? EOF : *p++;
}
inline int rint() {
int x = 0; char s = fgc();
for ( ; s < '0' || '9' < s; s = fgc() );
for ( ; '0' <= s && s <= '9'; s = fgc() ) x = x * 10 + ( s ^ '0' );
return x;
}
template<typename Tp>
inline void wint( Tp x ) {
if ( x < 0 ) putchar( '-' ), x = -x;
if ( 9 < x ) wint( x / 10 );
putchar( x % 10 ^ '0' );
}
typedef long long LL;
template<typename Tp>
inline void chkmin( Tp& a, const Tp b ) { b < a && ( a = b ); }
template<typename Tp>
inline void chkmax( Tp& a, const Tp b ) { a < b && ( a = b ); }
inline LL lmin( const LL a, const LL b ) { return a < b ? a : b; }
const int MAXN = 3e5, MAXM = MAXN << 1, MAXLG = 20;
const LL LINF = 1ll << 60;
int n, m;
template<const int NODE, const int EDGE>
struct Graph {
int ecnt, head[NODE], to[EDGE], nxt[EDGE];
LL len[EDGE];
Graph(): ecnt( 1 ) {}
inline void operator () ( const int s, const int t, const LL w ) {
#ifdef RYBY
printf( "%d %d %lld\n", s, t, w );
#endif
to[++ecnt] = t, len[ecnt] = w, nxt[ecnt] = head[s], head[s] = ecnt;
}
};
#define adj( t, u, v ) \
for ( int e = t.head[u], v; v = t.to[e], e; e = t.nxt[e] )
Graph<MAXN + 5, MAXM * 2 + 5> src;
int vnode, dfc, dfn[MAXN * 2 + 5], low[MAXN + 5];
LL pre[MAXN * 2 + 5];
Graph<MAXN * 2 + 5, MAXN * 2 + 5> cac;
inline void buildCactus( const int u, const int f ) {
static int top = 0, stk[MAXN + 5];
dfn[u] = low[u] = ++dfc, stk[++top] = u;
adj( src, u, v ) if ( v != f ) {
if ( !dfn[v] ) {
buildCactus( v, u );
chkmin( low[u], low[v] );
if ( low[v] >= dfn[u] ) {
cac( u, ++vnode, 0 ), pre[vnode] = src.len[e];
int las = u, ttop = top, cnt = 0;
do {
int w = stk[top];
for ( int i = src.head[w]; i; i = src.nxt[i] ) {
if ( i ^ e ^ 1 && src.to[i] == las ) {
++cnt;
pre[w] = pre[las] + src.len[i];
pre[vnode] += src.len[i];
break;
}
}
las = w;
} while ( stk[top--] != v );
do {
int w = stk[ttop];
cac( vnode, w, cnt ?
lmin( pre[w], pre[vnode] - pre[w] ) : pre[vnode] );
} while ( stk[ttop--] != v );
}
} else chkmin( low[u], dfn[v] );
}
}
// `dfc` and `dfn` was used by `buildCactus`, pay attention.
int dep[MAXN * 2 + 5], fa[MAXN * 2 + 5][MAXLG + 5];
LL dis[MAXN * 2 + 5];
inline void initCactus( const int u ) {
dfn[u] = ++dfc;
for ( int i = 1; fa[u][i - 1]; fa[u][i] = fa[fa[u][i - 1]][i - 1], ++i );
adj( cac, u, v ) {
dep[v] = dep[u] + 1, dis[v] = dis[u] + cac.len[e], fa[v][0] = u;
initCactus( v );
}
}
inline int lca( int u, int v ) {
if ( dep[u] < dep[v] ) u ^= v ^= u ^= v;
per ( i, MAXLG, 0 ) if ( dep[fa[u][i]] >= dep[v] ) u = fa[u][i];
if ( u == v ) return u;
per ( i, MAXLG, 0 ) if ( fa[u][i] != fa[v][i] ) u = fa[u][i], v = fa[v][i];
return fa[u][0];
}
inline int climb( int u, const int par ) {
per ( i, MAXLG, 0 ) if ( dep[fa[u][i]] > dep[par] ) u = fa[u][i];
return u;
}
Graph<MAXN * 2 + 5, MAXN * 2 + 5> virc;
inline void vlink( int s, int t ) {
// s is t's ancestor in cactus tree.
if ( s > n ) {
int is = climb( t, s );
virc( s, is, dis[is] - dis[s] ), s = is;
}
if ( t > n ) {
virc( fa[t][0], t, dis[t] - dis[fa[t][0]] ), t = fa[t][0];
}
if ( s != t ) virc( s, t, dis[t] - dis[s] );
}
inline void buildVirCac( std::vector<int>& vec ) {
static int top, stk[MAXN * 2 + 5];
virc.ecnt = 0, stk[top = 1] = 1;
std::sort( vec.begin(), vec.end(), []( const int a, const int b ) {
return dfn[a] < dfn[b];
} );
for ( int u: vec ) if ( u != 1 ) {
int anc = lca( stk[top], u );
while ( dep[stk[top]] > dep[anc] ) {
int a = stk[top--], b = dep[stk[top]] < dep[anc] ? anc : stk[top];
vlink( b, a );
}
if ( stk[top] != anc ) stk[++top] = anc;
stk[++top] = u;
}
while ( top > 1 ) {
int a = stk[top--], b = stk[top];
vlink( b, a );
}
}
LL ans, f[MAXN * 2 + 5];
bool book[MAXN + 5];
inline void contri( const int u, const std::vector<int>& cir ) {
static int que[MAXN + 5], hd, tl;
int sz = int( cir.size() ); LL half = pre[u] >> 1;
#define val( i ) ( pre[cir[i]] + ( i >= sz >> 1 ? pre[u] : 0 ) )
que[hd = tl = 1] = 0;
rep ( i, 1, sz - 1 ) {
while ( hd <= tl && val( i ) - val( que[hd] ) > half ) ++hd;
if ( hd <= tl ) {
chkmax( ans, f[cir[que[hd]]] - val( que[hd] )
+ f[cir[i]] + val( i ) );
}
while ( hd <= tl && f[cir[que[tl]]] - val( que[tl] )
<= f[cir[i]] - val( i ) ) --tl;
que[++tl] = i;
}
#undef val
}
inline void solve( const int u, const int par ) {
f[u] = -LINF;
adj( virc, u, v ) solve( v, u );
if ( u <= n ) {
LL mx = book[u] ? 0 : -LINF, sx = -LINF;
adj( virc, u, v ) {
if ( LL d = f[v] + virc.len[e]; d > mx ) sx = mx, mx = d;
else if ( d > sx ) sx = d;
}
chkmax( ans, mx + sx ), f[u] = mx;
} else {
static std::vector<int> cir; cir.clear();
LL tmpp = pre[par]; pre[par] = 0;
cir.push_back( par );
adj( virc, u, v ) cir.push_back( v );
int sz = int( cir.size() );
cir.resize( sz << 1 );
rep ( i, 0, sz - 1 ) cir[sz + i] = cir[i];
contri( u, cir );
pre[par] = tmpp;
adj( virc, u, v ) chkmax( f[u], f[v] + virc.len[e] );
}
virc.head[u] = 0;
}
int main() {
n = rint(), m = rint();
rep ( i, 1, m ) {
int u = rint(), v = rint(), w = rint();
src( u, v, w ), src( v, u, w );
}
#ifdef RYBY
puts( "+++ +++ +++" );
#endif
vnode = n, buildCactus( 1, 0 );
dfc = 0, dep[1] = 1, initCactus( 1 );
#ifdef RYBY
puts( "--- --- ---" );
#endif
std::vector<int> vec;
for ( int q = rint(), k; q--; vec.clear() ) {
k = rint(), vec.resize( k );
rep ( i, 0, k - 1 ) book[vec[i] = rint()] = true;
buildVirCac( vec );
ans = 0, solve( 1, 0 );
wint( ans ), putchar( '\n' );
for ( int u: vec ) book[u] = false;
}
return 0;
}
Solution -「UOJ #87」mx 的仙人掌的更多相关文章
- Solution -「UOJ #46」玄学
\(\mathcal{Description}\) Link. 给定序列 \(\{a_n\}\) 和 \(q\) 次操作,操作内容如下: 给出 \(l,r,k,b\),声明一个修改方案,表示 ...
- Solution -「UOJ #450」复读机
\(\mathcal{Description}\) Link. 求从 \(m\) 种颜色,每种颜色无限多的小球里选 \(n\) 个构成排列,使得每种颜色出现次数为 \(d\) 的倍数的排列方案 ...
- Solution -「ARC 104E」Random LIS
\(\mathcal{Description}\) Link. 给定整数序列 \(\{a_n\}\),对于整数序列 \(\{b_n\}\),\(b_i\) 在 \([1,a_i]\) 中等概率 ...
- Solution -「UNR #5」「UOJ #671」诡异操作
\(\mathcal{Desciprtion}\) Link. 给定序列 \(\{a_n\}\),支持 \(q\) 次操作: 给定 \(l,r,v\),\(\forall i\in[l,r], ...
- Solution -「JOISC 2020」「UOJ #509」迷路的猫
\(\mathcal{Decription}\) Link. 这是一道通信题. 给定一个 \(n\) 个点 \(m\) 条边的连通无向图与两个限制 \(A,B\). 程序 Anthon ...
- Solution -「UR #21」「UOJ #632」挑战最大团
\(\mathcal{Description}\) Link. 对于简单无向图 \(G=(V,E)\),定义它是"优美"的,当且仅当 \[\forall\{a,b,c,d\ ...
- Solution -「UR #2」「UOJ #32」跳蚤公路
\(\mathcal{Description}\) Link. 给定一个 \(n\) 个点 \(m\) 条边的带权有向图,每条边还有属性 \(s\in\{-1,0,1\}\).对于每个 \(u ...
- Solution -「CTS 2019」「洛谷 P5404」氪金手游
\(\mathcal{Description}\) Link. 有 \(n\) 张卡牌,第 \(i\) 张的权值 \(w_i\in\{1,2,3\}\),且取值为 \(k\) 的概率正比于 \ ...
- Solution -「BZOJ 3812」主旋律
\(\mathcal{Description}\) Link. 给定含 \(n\) 个点 \(m\) 条边的简单有向图 \(G=(V,E)\),求 \(H=(V,E'\subseteq E)\ ...
随机推荐
- JS 利用新浪接口通过IP地址获取当前所在城市
<html xmlns="http://www.w3.org/1999/xhtml"><head runat="server">< ...
- Spark词频前十的统计练习
注:图片如果损坏,点击文章链接:https://www.toutiao.com/i6815390070254600712/ 承接上一个文档<Spark本地环境实现wordCount单词计数> ...
- VUE3 之 表单元素
1. 概述 老话说的好:行动起来,原地观望是没有用的. 言归正传,今天我们来聊聊 VUE3 的 表单元素. 2. 表单元素 2.1 文本框与数据绑定 <body> <div id=& ...
- 《剑指offer》面试题42. 连续子数组的最大和
问题描述 输入一个整型数组,数组里有正数也有负数.数组中的一个或连续多个整数组成一个子数组.求所有子数组的和的最大值. 要求时间复杂度为O(n). 示例1: 输入: nums = [-2,1,-3,4 ...
- 《剑指offer》面试题44. 数字序列中某一位的数字
问题描述 数字以0123456789101112131415-的格式序列化到一个字符序列中.在这个序列中,第5位(从下标0开始计数)是5,第13位是1,第19位是4,等等. 请写一个函数,求任意第n位 ...
- 使用Rainbond打包业务模块,实现业务积木式拼装
背景 每个程序员在学习开发的过程中,都知道解耦和模块化的重要性,也希望自己设计和开发的程序支持模块化,开发好的模块其他人就能快速复用,为了达成这个效果,我们学习各种模块化和解耦的技术,从面向对象的设计 ...
- .NET 云原生架构师训练营(权限系统 RGCA 架构设计)--学习笔记
目录 项目核心内容 实战目标 RGCA 四步架构法 项目核心内容 无代码埋点实现对所有 API Action 访问控制管理 对 EF Core 实体新增.删除.字段级读写控制管理 与 Identity ...
- 微信小程序-国际化(miniprogram-i18n)
前情提要 最近维护了一个微信小程序的老项目,维护的其中一项是添加国际化.由于踩了蛮多坑,所以就有了这篇文档!!! miniprogram-i18n 对除小程序外的其他框架开发做过国际化的朋友来说i18 ...
- golang中channel
1. Channel是Go中的一个核心类型,你可以把它看成一个管道,通过它并发核心单元就可以发送或者接收数据进行通讯(communication). 2. select package main im ...
- 【Vue源码学习】响应式原理探秘
最近准备开启Vue的源码学习,并且每一个Vue的重要知识点都会记录下来.我们知道Vue的核心理念是数据驱动视图,所有操作都只需要在数据层做处理,不必关心视图层的操作.这里先来学习Vue的响应式原理,V ...