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. 【3】TensorFlow光速入门-训练及评估

    本文地址:https://www.cnblogs.com/tujia/p/13862357.html 系列文章: [0]TensorFlow光速入门-序 [1]TensorFlow光速入门-tenso ...

  2. MYSQL 那些事

    1.一条update语句 1.先通过引擎找到对应的行数据,并加锁 2.对行数据进行修改并调用引擎接口修改这条数据,然后释放锁(此时并没有把数据在磁盘上做出修改) 3.redo log在内存中生成这条u ...

  3. day78:luffy:前端对于token的认证&滑动验证码的实现

    目录 1.前端对于token的认证 2.滑动验证码 1.滑动验证码实现的原理 2.滑动验证码的代码实现 1.配置文件 2.前端实现:Login.vue 3.后端实现:改写jwt代码 1.前端对于tok ...

  4. Java 运行时动态生成class

    转载 http://www.liaoxuefeng.com/article/0014617596492474eea2227bf04477e83e6d094683e0536000 Java是一门静态语言 ...

  5. 测试TwemProxy的应知应会

    一.背景 最近中间件开发组对twemproxy的发现注册机制做了改造,之前没有接触过twemproxy,借这次测试的机会,初步学习了一下twemproxy相关的知识:下面用"测试语言&quo ...

  6. 企业级docker-registry原生镜像仓库高可用部署

    简介: 私有镜像仓库可以方便企业,或个人开发者共享内部镜像而不会泄漏私有代码,而且可以加速镜像的拉取.能更加方便得集成到容器化的 CI/CD 中去.也可建立自己的公共镜像仓库. 优势: Docker ...

  7. Redis还可以做哪些事?

    在上一篇文章中,讲到了redis五大基本数据类型的使用场景,除了string,hash,list,set,zset之外,redis还提供了一些其他的数据结构(当然,严格意义上也不算数据结构),一起来看 ...

  8. 2020 年TI 杯大学生电子设计竞赛E题总结(放大器非线性失真研究装置)

    2020年TI杯大学生电子设计竞赛E题总结(放大器非线性失真研究装置) 摘要:E题的竞赛内容主要是参赛者自己搭建一个晶体管放大器,能够产生不失真.顶部失真.底部失真.双向失真和交越失真五种波形,并分别 ...

  9. 300万运算/秒 :VoltDB在电信行业基准测试上可线性扩展性能

    01 总 体 概 述 VoltDB受到全球电信软件解决方案提供商的信赖,后者将其作为首选内存数据库来驱动他们部署在全球100多家运营商处的任务关键型应用.VoltDB受到青睐的原因在于其性能和功能不仅 ...

  10. c++11-17 模板核心知识(一)—— 函数模板

    1.1 定义函数模板 1.2 使用函数模板 1.3 两阶段翻译 Two-Phase Translation 1.3.1 模板的编译和链接问题 1.4 多模板参数 1.4.1 引入额外模板参数作为返回值 ...