Solution -「多校联训」战神归来
\(\mathcal{Description}\)
Link.
一条地铁线路上共 \(m\) 个站点,\(n\) 个人乘坐地铁,第 \(i\) 个人需要从 \(s_i\) 站坐到 \(e_i\) 站。你可以指挥他们在保证不走回头路的情况下走到某个站,或指挥处于同一个站的两人交换地铁卡。一张从 \(x\) 站进站 \(y\) 站出站的地铁卡花费为 \(|x-y|\),最小化花费和并给出可行方案。
\(n\le10^5\),\(m\le10^6\),方案步骤数 \(\le 4\times10^5\)。
\(\mathcal{Solution}\)
我切掉了,但没有完全切掉。
画画图,在数轴上,画出向右行动的蓝色箭头和向左行动的红色箭头。注意到换卡本质上是交换始发站点,那么重合的红蓝箭头是能相互抵消的!如图:
\(\vec{AB},\vec{CD}\) 互换起点,变为 \(\vec{AE},\vec{CF}\),长度之和减少 \(2|DF|\),并且 \(\vec{AE},\vec{FC}\) 仍旧能与其他向量进行互换操作。
不难证明,对于数轴上 \([k,k+1]\),覆盖它的红、蓝向量的数量的较小者就是这段区间被抵消的次数,且我们一定能构造方案取到这一下界,用向量总长减去它就求得最小花费。
考虑方案的构造,注意到次数限制 \(4\times10^5=4\max\{n\}\),猜测“抵消”操作能够在 \(n\) 次换卡操作内完成,那么每次换卡就得让一个向量无法再与其他向量抵消。理性分析,我们分别维护蓝色向量和红色向量的小根堆,按 \((\text{左侧点},\text{右侧点})\) 的偏序关系分别取出最小者,设为 \(\boldsymbol u,\boldsymbol v\)。若它们没有公共区间则必然有一个已经无用;否则,分类讨论:
\(e(\boldsymbol u)\le s(\boldsymbol v)\),即红色向量右端点靠右,如图:
\(\boldsymbol u=\vec{AB}\) 用完必然被丢掉,而 \(\vec{FD}\) 会被抵消,我们可以放心地让 \(A,C\) 在 \(B(F)\) 点会合换卡。但是!不能走回头路的限制带来一个问题:若存在 \(\vec{GH}\),\(C\) 就无法与 \(G\) 换卡了!
解决方法形象而自然:红色向量向左而扫描方向向右,那么红色向量上的会合点不得不会从左到右出现,那我们反过来以栈的顺序指挥红色向量不就好啦?
\(e(\boldsymbol u)>s(\boldsymbol v)\),即蓝色向量的右端靠右,与上个情况恰好相反,我们需要立即将操作方案加入答案再做后续计算。
最后,复杂度 \(\mathcal O(n\log n)\) 就能构造好方案啦。
\(\mathcal{Code}\)
/* Clearink */
#include <queue>
#include <cstdio>
#include <vector>
#include <cassert>
#define rep( i, l, r ) for ( int i = l, rep##i = r; i <= rep##i; ++i )
#define per( i, r, l ) for ( int i = r, per##i = l; i >= per##i; --i )
inline int rint() {
int x = 0, s = getchar();
for ( ; s < '0' || '9' < s; s = getchar() );
for ( ; '0' <= s && s <= '9'; s = getchar() ) x = x * 10 + ( s ^ '0' );
return x;
}
template<typename Tp>
inline void wint( const Tp x ) {
if ( 9 < x ) wint( x / 10 );
putchar( x % 10 ^ '0' );
}
inline int imin( const int a, const int b ) { return a < b ? a : b; }
inline int imax( const int a, const int b ) { return a < b ? b : a; }
typedef long long LL;
const int MAXN = 1e5, MAXM = 1e6;
int n, m, s[MAXN + 5], e[MAXN + 5], fin[MAXN + 5];
struct Atom {
int l, r, id;
inline bool operator < ( const Atom& t ) const {
return !( l != t.l ? l <= t.l : ( r != t.r ? r <= t.r : id <= t.id ) );
}
};
std::priority_queue<Atom> seg[2];
std::vector<Atom> plan;
inline void action( const Atom& u, const Atom& v, const int p ) {
if ( u.l != p ) plan.push_back( { u.id, p, 0 } );
if ( v.r + 1 != p ) plan.push_back( { v.id, p, 0 } );
plan.push_back( { u.id, v.id, 1 } );
}
inline void solve( LL& ans ) { // strange...
if ( seg[0].empty() || seg[1].empty() ) return ;
Atom u( seg[0].top() ), v( seg[1].top() );
if ( u.r < v.l ) seg[0].pop(), solve( ans );
else if ( v.r < u.l ) seg[1].pop(), solve( ans );
else {
seg[0].pop(), seg[1].pop();
int il = imax( u.l, v.l ), ir = imin( u.r, v.r );
ans -= ir - il + 1 << 1;
if ( u.r <= v.r ) {
if ( u.r < v.r ) seg[1].push( { u.r + 1, v.r, v.id } );
solve( ans ), action( u, v, u.r + 1 );
} else {
if ( v.r < u.r ) seg[0].push( { v.r + 1, u.r, u.id } );
action( u, v, v.r + 1 ), solve( ans );
}
}
}
int main() {
freopen( "subway.in", "r", stdin );
freopen( "subway.out", "w", stdout );
for ( int T = rint(); T--; ) {
n = rint(), m = rint();
for ( ; !seg[0].empty(); seg[0].pop() );
for ( ; !seg[1].empty(); seg[1].pop() );
plan.clear();
LL ans = 0;
rep ( i, 1, n ) {
fin[i] = s[i] = rint(), e[i] = rint();
if ( s[i] < e[i] ) {
ans += e[i] - s[i], seg[0].push( { s[i], e[i] - 1, i } );
} else {
ans += s[i] - e[i], seg[1].push( { e[i], s[i] - 1, i } );
}
}
solve( ans );
wint( ans ), putchar( ' ' );
for ( const Atom& a: plan ) if ( !a.id ) fin[a.l] = a.r;
rep ( i, 1, n ) if ( fin[i] != e[i] ) {
plan.push_back( { i, e[i], 0 } );
}
assert( plan.size() <= 4e5 );
wint( plan.size() ), putchar( '\n' );
for ( const Atom& a: plan ) {
wint( a.id ), putchar( ' ' );
wint( a.l ), putchar( ' ' );
wint( a.r ), putchar( '\n' );
}
}
return 0;
}
\(\mathcal{Details}\)
挺逗的,考场上想到了逆序操作却忽略了一部分操作需要正序,“对称”的坑点只注意到一个……补题发现把循环换成代码里的递归再调一调语句顺序就过了。长记性呐。
Solution -「多校联训」战神归来的更多相关文章
- Solution -「多校联训」排水系统
\(\mathcal{Description}\) Link. 在 NOIP 2020 A 的基础上,每条边赋权值 \(a_i\),随机恰好一条边断掉,第 \(i\) 条段的概率正比于 \(a ...
- Solution -「多校联训」I Love Random
\(\mathcal{Description}\) 给定排列 \(\{p_n\}\),可以在其上进行若干次操作,每次选取 \([l,r]\),把其中所有元素变为原区间最小值,求能够得到的所有不同序 ...
- Solution -「多校联训」签到题
\(\mathcal{Description}\) Link. 给定二分图 \(G=(X\cup Y,E)\),求对于边的一个染色 \(f:E\rightarrow\{1,2,\dots,c\ ...
- Solution -「多校联训」朝鲜时蔬
\(\mathcal{Description}\) Link. 破案了,朝鲜时蔬 = 超现实树!(指写得像那什么一样的题面. 对于整数集 \(X\),定义其 好子集 为满足 \(Y\sub ...
- Solution -「多校联训」消失的运算符
\(\mathcal{Description}\) Link. 给定长度为 \(n\) 的合法表达式序列 \(s\),其中数字仅有一位正数,运算符仅有 - 作为占位.求将其中恰好 \(k\) ...
- Solution -「多校联训」假人
\(\mathcal{Description}\) Link. 一种物品有 长度 和 权值 两种属性,现给定 \(n\) 组物品,第 \(i\) 组有 \(k_i\) 个,分别为 \((1,a ...
- Solution -「多校联训」古老的序列问题
\(\mathcal{Description}\) Link. 给定序列 \(\{a_n\}\),和 \(q\) 次形如 \([L,R]\) 的询问,每次回答 \[\sum_{[l,r]\su ...
- Solution -「多校联训」Sample
\(\mathcal{Description}\) Link (稍作简化:)对于变量 \(p_{1..n}\),满足 \(p_i\in[0,1],~\sum p_i=1\) 时,求 \(\ma ...
- Solution -「多校联训」光影交错
\(\mathcal{Description}\) Link. 一个游戏包含若干次卡牌抽取,每次以 \(p_l\) 的概率得到 \(+1\),\(p_d\) 的概率得到 \(-1\),否则得到 ...
随机推荐
- 移动端开发,rem单位妙用,自适应
其实我们都会,但是不会用 大家看到的都是,这样的,这样的. http://www.woshipm.com/ucd/24110.html http://www.w3cplus.com/css3/defi ...
- 硬核 - Java 随机数相关 API 的演进与思考(下)
本系列将 Java 17 之前的随机数 API 以及 Java 17 之后的统一 API 都做了比较详细的说明,并且将随机数的特性以及实现思路也做了一些简单的分析,帮助大家明白为何会有这么多的随机数算 ...
- h5跳转高德地图
<a href="https://uri.amap.com/marker?position=经度,纬度&name=所在的位置名称">高德地图</a>
- Sharding Sphere的分库分表
什么是 ShardingSphere? 1.一套开源的分布式数据库中间件解决方案 2.有三个产品:Sharding-JDBC 和 Sharding-Proxy 3.定位为关系型数据库中间件,合理在分布 ...
- Spark-寒假-实验3
1.安装 Hadoop 和 Spark 进入 Linux 系统,参照本教程官网"实验指南"栏目的"Hadoop 的安装和使用",完成 Hadoop 伪分布式模式 ...
- 安全检测服务如何帮助社交类App提升应用自身和用户个人安全
社交类App如今人手必备,且大部分功能.业务活动和产品价值均与用户紧密联系,流量的多少甚至影响着一款应用的生命周期.因此,开发者们开始关注内容合规.治理黑产.防盗防爬等应用安全方面的能力.识别虚假流量 ...
- 【一个小实验】腾讯云的redis的主从结构的特性
使用腾讯云上的redis,可以添加多个备机的分片,并且可以选择不同的账号来设定主从读写的策略. 现在设置两个账号:primary-主节点写,主节点读:secondary-主节点写,从节点读. 研究出了 ...
- HBase之MinorCompact全程解析
转自:https://blog.csdn.net/u014297175/article/details/50456147 Compact作用 当MemStore超过阀值的时候,就要flush到HDFS ...
- golang中for循环的常用用法
package main import "fmt" func main() { //printFormula() // 打印九九乘法表 //printLeftTriangle() ...
- golang中结构体和结构体指针的内存管理
p1是结构体,p2是结构体指针. 2. 声明并赋值结构体和结构体指针 package main import "fmt" type Person struct { name str ...