2020 CSP-J 多校赛 Day 2 T2 题解
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;
最后,贪心总策略即是把 part1 与 part2 操作下来取最小值。
环上依然每个数一定会被交换一次,最小点会在被交换一次(将环外最小边拉入环的最小代价),这时环中就有了 \(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 题解的更多相关文章
- HZNU第十二届校赛赛后补题
愉快的校赛翻皮水! 题解 A 温暖的签到,注意用gets #include <map> #include <set> #include <ctime> #inclu ...
- xdoj 2020校赛复盘
平时写东西都不喜欢复盘,这肯定不是一个好习惯,感觉每次花好几个小时甚至好几天写题目然后没写出来也不去看题解是一种很蠢的行为( 花了这么久时间打校赛,虽然水平很low,数据结构也不太会用,还是记录一下自 ...
- 2016 华南师大ACM校赛 SCNUCPC 非官方题解
我要举报本次校赛出题人的消极出题!!! 官方题解请戳:http://3.scnuacm2015.sinaapp.com/?p=89(其实就是一堆代码没有题解) A. 树链剖分数据结构板题 题目大意:我 ...
- 2014上半年acm总结(1)(入门+校赛)
大一下学期才开始了acm,不得不说有一点迟,但是acm确实使我的生活充实了很多,,不至于像以前一样经常没事干= = 上学期的颓废使我的c语言学的渣的一笔..靠考前突击才基本掌握了语法 寒假突然醒悟, ...
- 牛客网多校赛第9场 E-Music Game【概率期望】【逆元】
链接:https://www.nowcoder.com/acm/contest/147/E 来源:牛客网 时间限制:C/C++ 1秒,其他语言2秒 空间限制:C/C++ 262144K,其他语言524 ...
- 2014哈商大ICPC/ACM校赛解题报告
被debug邀请去參加校赛,哎,被虐..我对不起工大.. 由于本人不搞ACM,算法处于HelloWorld水准.. 虽然题目除了鸟不拉屎星人之外都非常水,但我能做到这个程度,全然是超水平发挥了.. 数 ...
- ZJU 17th 校赛
第一次参加校赛,和小伙伴们拿了7个气球,还是挺开心的. 简单记个流水账吧. A:判断出INF的情况后 暴力模拟即可. INF的情况有x=1 || y=1 || (x==2 && y= ...
- ZOJ 3955 Saddle Point 校赛 一道计数题
ZOJ3955 题意是这样的 给定一个n*m的整数矩阵 n和m均小于1000 对这个矩阵删去任意行和列后剩余一个矩阵为M{x1,x2,,,,xm;y1,y2,,,,,yn}表示删除任意的M行N列 对于 ...
- Comet OJ 夏季欢乐赛 篮球校赛
Comet OJ 夏季欢乐赛 篮球校赛 题目传送门 题目描述 JWJU注重培养学生的"唱,跳,rap,篮球"能力.于是每年JWJU都会举办篮球校赛,来给同学们一个切磋篮球技术的平台 ...
随机推荐
- SpringBoot整合redis简单实现
1.创建springboot项目 pom.xml <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi=& ...
- DevOps 视角的前后端分离与实战
本文作者:CODING - 廖红坤 前言 随着微前端.微服务等技术理念和架构的蓬勃发展,我们已经没必要去讨论为什么要前后端分离这种话题,前后端分离已成为互联网项目开发的标准模式.前后端在各自的领域发展 ...
- python栈、队列、文件目录遍历
一. 栈与队列 关注公众号"轻松学编程"了解更多. 1. 栈 stack 特点:先进先出[可以抽象成竹筒中的豆子,先进去的后出来] 后来者居上 mystack = [] #压栈[向 ...
- Django项目-个人网站之事项模块
Django项目之个人网站 关注公众号"轻松学编程"了解更多. Github地址:https://github.com/liangdongchang/MyWeb.git 感兴趣的可 ...
- Java_静态代理与Lambda
静态代理 要点: 公共接口 真实角色 代理角色 public class StaticProxy { public static void main(String[] args) { You you ...
- sdsdsd
create PROCEDURE b2(in c_year int,in co int)begin declare num int; if exists(select * from class whe ...
- 【转载】TCP/IP协议栈
TCP/IP 协议栈是一系列网络协议的总和,是构成网络通信的核心骨架,它定义了电子设备如何连入因特网,以及数据如何在它们之间进行传输.TCP/IP 协议采用4层结构,分别是应用层.传输层.网络层和链路 ...
- Windows defender历史记录闪退解决方案
删除C:\ProgramData\Microsoft\Windows defender\Scans\History\Service文件夹 另外defender可以设置保护文件夹,选择病毒和威胁防护-管 ...
- C#调用pyd
python打包成pyd在本篇博客不多叙述,请读者自行百度,本篇博客主要讲解在C#中如何调用pyd以及遇到的一些问题如何解决. 1.安装pythonnet pythonnet是一个强大的工具包,用于C ...
- c++中的几种函数调用约定(转)
C++中的函数调用约定(调用惯例)主要针对三个问题: 1.参数传递的方式(是否采用寄存器传递参数.采用哪个寄存器传递参数.参数压桟的顺序等): 参数的传递方式,最常见的是通过栈传递.函数的调用方将参数 ...