0x01 引入

在考场时想了一个错误算法,口胡一下,或许对理解正解有点帮助。

我们考虑交换两个数产生的代价,你会发现我们需要让大的数重复被交换的次数尽可能少,减少它对后面的代价。

那么不难构思出一个按从大到小的顺序将每个数一步交换到应到的位置的算法。

bool cmp(node u, node v) { return u.x > v.x; }
int main() {
int n = read();
for (int i = 1; i <= n; i++) {
a[i].x = read();
a[i].index = i; // 保存它在原数组里的位置
b[i] = a[i].x;
}
sort(a + 1, a + n + 1, cmp); // 排序
for (int i = 1; i <= n; i++) { // 从大到小处理
if (a[i].x == b[n - i + 1])
continue;
ans += (a[i].x + b[n - i + 1]); // 累加答案(这个数和它之前所在的序列的那个数对应的排序后的位置上的数的和
Swap(b[a[i].index], b[n - i + 1]);
// 记得交换
}
printf("%lld\n", ans);
return 0;
}

不过下来被自己hack掉了,因为这个算法考虑了最大的代价最小,但很有可能让后几个数产生的代价更大(重复交换次数更多),所以这是一个错误算法

0x02 正解

14在题解里引入了图论:(原文)考虑对序列 \(a\) 从小到大排序得到序列 \(b\) ,对于每一个 \(i\) 我们进行连边,将位置 \(i\) 向 \(a_i\) 在序列 \(b\) 出现的位

置连一条有向边

其实就是将我的那个错误算法里,需要交换的对应点连上有向边。则这个图一定由无数个环组成

毕竟这个序列是固定的,也就是说在排序后的序列里还是原序列里的那些数,那么如果要将原数列变换成有序的,一定会将某几个区间里的数进行轮换,才能满足,而如果把每次交换看做 \(A-B\),那么区间内轮换及是 \(A-B\),\(B-C\),\(C-A\)。其实就是环嘛。

举个例子:

a[] = 3 1 2 4 ----> b[] = 1 2 3 4 // 会发现就是元素 1 2 3 轮换。

图:

1 to 3, 2 to 1, 3 to 2, 4 to 4

由两个环(其中一个为自环)组成。

不难通过例子发现,如果连边后出现自环,那么就代表当前这个元素处于正确位置无需再交换了。

于是题目变为:给定某一些环,每次消耗某些代价将其变为 \(n\) 个自环。

利用引入,依然考虑如何贪心实现。

part1:对于每个环不难想到每次变换使用可以用的代价(权值)最小的边。

即:有环

1 --4--> 3, 2 --3--> 1, 3 --5--> 2;

我们将其变换为:(在除最小边外其余边中交换权值最小边的端点。

1 --4--> 3, 2 --3--> 2, 3 --5--> 1;

这时2出现自环,表示2到达需要到的位置了。1,3交换同上。最后一定能在part1条件下拿出最优解。

环上每个数一定会被交换到一次,所以代价为其和,在由于需要满足将其变为自环代价最小。在变成 \(cnt\) 个自环后,最小代价点的代价共被加了 \(cnt - 2\) 次( \(cnt\) 表示环上点的个数,排除最小点自己,排除最后一个自环不需交换,最后就是累加 \(cnt - 2\) 次)

sum + (cnt - 2) * k.

part2:不过很显然没有说不能环外与环内的交换,去让圆环权值变小,所以我们同样需要考虑对于每个环将环外的最小边加入当前这个环。

也就是:我们有两环:

1 --4--> 3, 2 --3--> 1, 3 --5--> 2, 4 --9--> 5, 5 --9--> 4;

对于 1-2-3 这个环我们将环外的最小值拉进来,尝试让当前环的代价再次变小。交换 1-4(权值最小)得:

1 --4--> 3, 2 --3--> 4, 3 --5--> 2, 4 --9--> 5, 5 --9--> 1;

最后,贪心总策略即是把 part1part2 操作下来取最小值。

环上依然每个数一定会被交换一次,最小点会在被交换一次(将环外最小边拉入环的最小代价),这时环中就有了 \(cnt + 1\) 个点,全部利用环外最小点进行更新。

sum + k + mi * (cnt + 1).

当然从面对数据编程这个层次,此题是需要离散化的。

实现:

#include <bits/stdc++.h>
using namespace std; typedef long long LL;
const int INF = 0x3f3f3f3f;
inline int Min(int x, int y) { return x < y ? x : y; }
const int MAXN = 1e6 + 5;
int a[MAXN], t[MAXN];
bool vis[MAXN]; struct node {
int index, w;
node() {}
friend bool operator<(node a, node b) { return a.w < b.w; }
} s[MAXN]; int main() {
int n, mi = INF;
scanf("%d", &n);
for (int i = 1; i <= n; i++) {
scanf("%d", &a[i]);
mi = min(mi, a[i]);
s[i].index = i;
s[i].w = a[i];
}
sort(s + 1, s + n + 1);
for (int i = 1; i <= n; i++) t[s[i].index] = i;
LL ans = 0;
for (int i = 1; i <= n; i++) {
if (vis[i])
continue;
LL cnt = 1, sum = a[i];
int k = INF;
vis[i] = true;
for (int j = t[i]; j != i; j = t[j]) { // 跑环
vis[j] = true;
cnt++; // 求出环山上有多少点
sum += a[j]; // 环上数的总和(与代价相关
k = min(k, a[j]); // 求出环上最小边的端点
}
if (cnt > 1) // 不是自环
ans += min(sum + (cnt - 2) * k, sum + k + mi * (cnt + 1));
// 分别计算 part1, part2
}
printf("%lld\n", ans);
return 0;
}

2020 CSP-J 多校赛 Day 2 T2 题解的更多相关文章

  1. HZNU第十二届校赛赛后补题

    愉快的校赛翻皮水! 题解 A 温暖的签到,注意用gets #include <map> #include <set> #include <ctime> #inclu ...

  2. xdoj 2020校赛复盘

    平时写东西都不喜欢复盘,这肯定不是一个好习惯,感觉每次花好几个小时甚至好几天写题目然后没写出来也不去看题解是一种很蠢的行为( 花了这么久时间打校赛,虽然水平很low,数据结构也不太会用,还是记录一下自 ...

  3. 2016 华南师大ACM校赛 SCNUCPC 非官方题解

    我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...

  4. 2014上半年acm总结(1)(入门+校赛)

    大一下学期才开始了acm,不得不说有一点迟,但是acm确实使我的生活充实了很多,,不至于像以前一样经常没事干=  = 上学期的颓废使我的c语言学的渣的一笔..靠考前突击才基本掌握了语法 寒假突然醒悟, ...

  5. 牛客网多校赛第9场 E-Music Game【概率期望】【逆元】

    链接:https://www.nowcoder.com/acm/contest/147/E 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524 ...

  6. 2014哈商大ICPC/ACM校赛解题报告

    被debug邀请去參加校赛,哎,被虐..我对不起工大.. 由于本人不搞ACM,算法处于HelloWorld水准.. 虽然题目除了鸟不拉屎星人之外都非常水,但我能做到这个程度,全然是超水平发挥了.. 数 ...

  7. ZJU 17th 校赛

    第一次参加校赛,和小伙伴们拿了7个气球,还是挺开心的.  简单记个流水账吧. A:判断出INF的情况后 暴力模拟即可. INF的情况有x=1 || y=1 || (x==2 && y= ...

  8. ZOJ 3955 Saddle Point 校赛 一道计数题

    ZOJ3955 题意是这样的 给定一个n*m的整数矩阵 n和m均小于1000 对这个矩阵删去任意行和列后剩余一个矩阵为M{x1,x2,,,,xm;y1,y2,,,,,yn}表示删除任意的M行N列 对于 ...

  9. Comet OJ 夏季欢乐赛 篮球校赛

    Comet OJ 夏季欢乐赛 篮球校赛 题目传送门 题目描述 JWJU注重培养学生的"唱,跳,rap,篮球"能力.于是每年JWJU都会举办篮球校赛,来给同学们一个切磋篮球技术的平台 ...

随机推荐

  1. Win32之创建进程

    CreateProcess函数介绍 BOOL CreateProcessA( LPCSTR lpApplicationName, //可执行文件的名称完整的路径+程序名字) LPSTR lpComma ...

  2. this.$nextTick的用法

    element-ui中有一个动态添加tag的组件,平常状态是按钮,点击后变成输入框:那么这里的话,希望按钮点击后,input输入框被选中 这里二者的切换依赖于inputVisible这个值,当按钮点击 ...

  3. 【总结】springcloud

    一.spirngcloud概述 1.springcloud是什么? spring cloud是一个一站式的开发分布式系统的框架,为开发人员提供了快速构建分布式系统中一些常见模式的工具(如:配置管理,服 ...

  4. NB-IoT的PSM模式有什么优点

    在NB-IoT系统中,PSM模式的优点是可进行长时间休眠,缺点是对终端接收(Mobile Terminated,MT)业务响应不及时,主要应用于远程抄表等对下行实时性要求不高的业务.实际上,物联网设备 ...

  5. NB-IoT的数据链路层和上行传输信道类型

    NB-IoT的数据链路层 NB-IoT在LTE系统的基础上对数据链路层进行了大量简化,但整体上还是保持了原有的框架.数据链路层是二层协议,包含了3个子层:MAC子层.RLC子层和PDCP子层. 以网络 ...

  6. [Luogu P3469] [POI2008]BLO-Blockade (割点)

    题面 传送门:https://www.luogu.org/problemnew/show/P3469 Solution 先跟我大声念: poi! 然后开始干正事. 首先,我们先把题目中的点分为两类:去 ...

  7. 团灭 LeetCode 打家劫舍问题

    有读者私下问我 LeetCode 「打家劫舍」系列问题(英文版叫 House Robber)怎么做,我发现这一系列题目的点赞非常之高,是比较有代表性和技巧性的动态规划题目,今天就来聊聊这道题目. 打家 ...

  8. 文件流转blob并播放

    axios 这里是请求了个mp3做例子: this.$axios({ methods:"GET", url:"/api/music/soures/双笙.mp3" ...

  9. 使用pipenv管理python虚拟环境

    前言 近期的项目中,我开始尝试着从virtualenv管理python虚拟环境,切换到用pipenv来管理. 经过一段时间的使用,着实觉得pipenv使用的更加顺手,更加的便捷.这当然也延续了 Ken ...

  10. 基于CSS3伪元素和动画绘制旋转太极图

    通过CSS3的动画知识来完成一个旋转的太极. 任务 1.创建一个div,用CSS控制其大小.边框.位置等,做成一个静态的圆形,一半为红色一半为白色. 2.用div的伪元素位置两个圆环并放置核实位置,使 ...