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. Libevent库基础(1)

    1.创建 eevent_base struct event_base *base = event_base_new(); 2.创建 事件event struct event *ev; struct e ...

  2. A. Cubes Sorting 解析(思維)

    Codeforce 1420 A. Cubes Sorting 解析(思維) 今天我們來看看CF1420 題目連結 題目 給一個數列\(a\),求能不能在不超過\(\frac{n(n-1)}{2}-1 ...

  3. C. Vladik and Memorable Trip 解析(思維、DP)

    Codeforce 811 C. Vladik and Memorable Trip 解析(思維.DP) 今天我們來看看CF811C 題目連結 題目 給你一個數列,一個區段的數列的值是區段內所有相異數 ...

  4. requestS模块发送请求的时候怎么传递参数

    首先要确定接口的传递参数是什么类型的,如果接口是查询,使用get请求方法,传递参数的时候使用params, 如果接口需要的json型参数的话,使用json,如果是上传文件的话,通过files参数在传递 ...

  5. java查询elasticsearch聚合

    java查es多分组聚合: SearchRequestBuilder requestBuilderOfLastMonth = transportClient.prepareSearch(TYPE_NA ...

  6. Hadoop高可用

    一.原因 - NameNode是HDFS的黑心配置HDFS有事hadoop的核心组件 NameNode 在Hadoop及群众至关重要 - NameNode的宕机导致集群的不可用 二.解决方案 其中 N ...

  7. Java才是世界上最好的语言,Java在高频交易中替代C++

    高频交易 高频交易是指从那些人们无法利用的极为短暂的市场变化中寻求获利的计算机化交易,比如,某种证券买入价和卖出价差价的微小变化,或者某只股票在不同交易所之间的微小价差.在高频交易中,自动化应用程序每 ...

  8. 安利下PyAUtoGUI这个库,可自动化控制鼠标键盘

    PyAutoGUI 不知道你有没有用过,它是一款用Python自动化控制键盘.鼠标的库.但凡是你不想手动重复操作的工作都可以用这个库来解决. 比如,我想半夜时候定时给发个微信,或者每天自动刷页面等操作 ...

  9. javascript播放带透明通道的mp4动画

    随着互联网的发展,动画效果也在一直更新,从刚开始的flsh动画,cocos骨骼动画,到YY开源的svga动画.最近1年来,带有透明通道的mp4动画被使用的极为广泛,对于app端.github上有开源的 ...

  10. day89:luffy:使用Celery完成我的订单超时取消&Polyv视频加密播放

    目录 1.我的订单超时取消 2.PoliV视频播放 1.我的订单超时取消 使用Celery完成超时取消功能 mycelery/order/tasks.py from mycelery.main imp ...