看到这题,我的第一反应是:这就是一个费用流模型?用模拟费用流的方法?

这应该是可以的,但是我忘记了怎么模拟费用流了IOI不可能考模拟费用流。于是我就想了另外一个方法。

首先我们考虑模拟费用流的模型如下图:

直接费用流复杂度比较大,我们把它换成一个dp。设\(f_{i, j}\)表示考虑了前\(i\)个点,且\(i\)个点后面一条在图中横着的边的流量为\(j\)的时候,最小费用是多少。注意这里从左到右的流量记为正,否则记为负。转移的时候如果第\(i\)个点是红点,就枚举\(S\)向这个点连的边的流量,否则枚举这个点向\(T\)连的边的流量。根据流量平衡方程我们算出前一条横着的边的流量。

用前缀/后缀min将这个算法优化至\(O((r + b)^2)\),可以获得\(7\)分的成绩。

代码如下:

#include "wiring.h"
#include <bits/stdc++.h>
using namespace std; const int N = 205, M = 205;
const long long inf = 1000000000000000ll; int n, m;
long long f[N + M][N + M << 2], g[N + M][N + M << 2];
pair<long long, int> vec[N + M]; long long min_total_length(std::vector<int> r, std::vector<int> b) {
n = r.size(), m = b.size(); for (int i = 1; i <= n; i++) vec[i] = make_pair(r[i - 1], 0);
for (int i = 1; i <= m; i++) vec[i + n] = make_pair(b[i - 1], 1);
sort(vec + 1, vec + n + m + 1); for (int i = 0; i <= (n + m << 2); i++) f[0][i] = inf;
f[0][n + m << 1] = 0ll; for (int i = 1; i <= n + m; i++) {
for (int j = 0; j <= (n + m << 2); j++) {
f[i][j] = f[i - 1][j];
if (i) {
int tim = j - (n + m << 1);
if (tim < 0) tim = -tim;
f[i][j] += 1ll * (vec[i].first - vec[i - 1].first) * tim;
}
}
if (vec[i].second) {
g[i][0] = f[i][0];
for (int j = 1; j <= (n + m << 2); j++) g[i][j] = min(f[i][j], g[i][j - 1]);
for (int j = 0; j <= (n + m << 2); j++) {
if (!j) f[i][j] = inf;
else f[i][j] = g[i][j - 1];
}
}
else {
g[i][n + m << 2] = f[i][n + m << 2];
for (int j = (n + m << 2) - 1; j >= 0; j--) g[i][j] = min(f[i][j], g[i][j + 1]);
for (int j = 0; j <= (n + m << 2); j++) {
if (j == (n + m << 2)) f[i][j] = inf;
else f[i][j] = g[i][j + 1];
}
}
}
return f[n + m][n + m << 1];
}

注意这里实现的时候用了平移的技巧处理第二维为负数的情况。

接下来我们考虑优化这个dp的方法。

把这个dp状态的第二维看成一个函数,那么我们会发现,需要进行的操作有:函数向左或向右平移一个单位,给它取前缀\(\min\),给它取后缀\(\min\),以及给它加上\(k \lvert x \rvert\)。

容易发现这些操作都不会改变函数下凸的性质。因此我们可以用APIO2016T2,我自己出的名为“穿越”的联测题等题目的方法。用一个set/multiset维护这个函数的每个拐点的位置以及斜率的变化值,再用\(O(1)\)的变量维护最左边/右边的那一段的斜率和截距,再维护偏移量(为了进行平移操作),就可以实现平移操作和加\(k \lvert x \rvert\)操作。而取前缀\(\min\)操作相当于是把一个函数图像的右边递增的一段变为常值函数,如下图所示:

因此我们可以在set/multiset上不断删除右边的拐点,直到右边那一段斜率刚好\(\ge 0\)(也就是再删去一个就\(<0\)了)为止。然后在改变恰好一个拐点的斜率变化量就可以实现前缀\(\min\)操作。同理我们可以实现后缀\(\min\)操作。

注意到这里的复杂度可以被拐点个数的减少量bound住,所以总复杂度仍然为\(O(n \log n)\)。

满分代码如下:

#include "wiring.h"
#include <bits/stdc++.h>
using namespace std; const int N = 100005, M = 100005;
const long long inf = 1000000000ll; int n, m;
pair<long long, int> vec[N + M];
multiset<pair<int, long long> > que; long long min_total_length(std::vector<int> r, std::vector<int> b) {
n = r.size(), m = b.size();
for (int i = 1; i <= n; i++) vec[i] = make_pair(r[i - 1], 0);
for (int i = 1; i <= m; i++) vec[i + n] = make_pair(b[i - 1], 1);
sort(vec + 1, vec + n + m + 1); int val = 0;
long long k_l = -inf, k_r = inf, val_l = inf * (n + m); que.insert(make_pair(0, inf << 1));
for (int i = 1; i <= n + m; i++) {
if (i > 1) {
que.insert(make_pair(val, (vec[i].first - vec[i - 1].first) << 1));
k_l -= vec[i].first - vec[i - 1].first, k_r += vec[i].first - vec[i - 1].first;
val_l += (vec[i].first - vec[i - 1].first) * (n + m + val);
}
if (vec[i].second) {
val++;
while (k_l < 0ll) {
pair<int, long long> pi = *que.begin();
if (k_l + pi.second < 0ll) {
val_l -= pi.second * (pi.first + n + m);
k_l += pi.second;
}
else {
val_l += k_l * (pi.first + n + m);
que.insert(make_pair(pi.first, k_l + pi.second));
k_l = 0ll;
}
que.erase(que.find(pi));
}
}
else {
val--;
while (k_r > 0ll) {
pair<int, long long> pi = *que.rbegin();
if (k_r - pi.second > 0ll) k_r -= pi.second;
else {
que.insert(make_pair(pi.first, pi.second - k_r));
k_r = 0ll;
}
que.erase(que.find(pi));
}
}
} int lst = -n - m;
long long ans = val_l;
for (multiset<pair<int, long long> > :: iterator it = que.begin(); it != que.end(); it++) {
pair<int, long long> pi = *it;
if (pi.first < val) {
ans += k_l * (pi.first - lst);
k_l += pi.second, lst = pi.first;
}
else {
ans += k_l * (val - lst);
lst = val;
break;
}
}
if (lst < val) ans += k_l * (val - lst);
return ans;
}

「IOI2017」接线 的另类做法的更多相关文章

  1. 「IOI2017」西默夫 的一个另类做法

    我们发现如果我们有一个环套树的话,那么我们可以把这个环套树去掉每一条环上的边\(e\),问一遍有多少御道在这棵树上.假设删去\(e\)后答案为\(A_e\). 如果答案全部一样,那么说明环上的边都不在 ...

  2. LOJ #2135. 「ZJOI2015」幻想乡战略游戏(点分树)

    题意 给你一颗 \(n\) 个点的树,每个点的度数不超过 \(20\) ,有 \(q\) 次修改点权的操作. 需要动态维护带权重心,也就是找到一个点 \(v\) 使得 \(\displaystyle ...

  3. JavaScript OOP 之「创建对象」

    工厂模式 工厂模式是软件工程领域一种广为人知的设计模式,这种模式抽象了创建具体对象的过程.工厂模式虽然解决了创建多个相似对象的问题,但却没有解决对象识别的问题. function createPers ...

  4. 「C++」理解智能指针

    维基百科上面对于「智能指针」是这样描述的: 智能指针(英语:Smart pointer)是一种抽象的数据类型.在程序设计中,它通常是经由类型模板(class template)来实做,借由模板(tem ...

  5. 「HNOI2016」数据结构大毒瘤

    真是 \(6\) 道数据结构毒瘤... 开始口胡各种做法... 「HNOI2016」网络 整体二分+树状数组. 开始想了一个大常数 \(O(n\log^2 n)\) 做法,然后就被卡掉了... 发现直 ...

  6. Loj #3057. 「HNOI2019」校园旅行

    Loj #3057. 「HNOI2019」校园旅行 某学校的每个建筑都有一个独特的编号.一天你在校园里无聊,决定在校园内随意地漫步. 你已经在校园里呆过一段时间,对校园内每个建筑的编号非常熟悉,于是你 ...

  7. 「HNOI2016」序列 解题报告

    「HNOI2016」序列 有一些高妙的做法,懒得看 考虑莫队,考虑莫队咋移动区间 然后你在区间内部找一个最小值的位置,假设现在从右边加 最小值左边区间显然可以\(O(1)\),最小值右边的区间是断掉的 ...

  8. 「ZJOI2015」地震后的幻想乡 解题报告

    「ZJOI2015」地震后的幻想乡 想了半天,打开洛谷题解一看,最高票是_rqy的,一堆密密麻麻的积分差点把我吓跑. 据说有三种解法,然而我只学会了一种最辣鸡的凡人解法. 题意:给一个无向图\(G\) ...

  9. 「TJOI2015」概率论 解题报告

    「TJOI2015」概率论 令\(f_i\)代表\(i\)个点树形态数量,\(g_i\)代表\(i\)个点叶子个数 然后列一个dp \[ f_i=\sum_{j=0}^{i-1} f_j f_{i-j ...

随机推荐

  1. .Net核心依赖项注入:生命周期和最佳实践

    在讨论.Net的依赖注入(DI)之前,我们需要知道我们为什么需要使用依赖注入 依赖反转原理(DIP): DIP允许您将两个类解耦,否则它们会紧密耦合,这有助于提高可重用性和更好的可维护性 DIP介绍: ...

  2. 云服务器-Ubuntu更新系统版本-更新Linux内核-服务器安全配置优化-防反弹shell

    购入了一台阿里云的ESC服务器,以前都用CentOS感觉Yum不怎么方便,这次选的Ubuntu16.04.7 搭好服务之后做安全检查,发现Ubuntu16.04版本漏洞众多:虽然也没有涉及到16.04 ...

  3. FL Studio中有关Sub Bass的一些制作与混音技巧

    1.Sub Bass是什么? Sub Bass是一种低沉的低音,其频率大约低于60赫兹,并向下延伸,包括人类所能听到的最低频率,约为20赫兹.在这个范围内,人类的听觉不是很灵敏,所以在这个范围内的声音 ...

  4. css3系列之linear-gradient() repeating-linear-gradient() 和 radial-gradient() repeating-radial-gradient()

    linear-gradient()  (线性渐变) repeating-linear-gradient()   (重复的线性渐变) radial-gradient()  (镜像渐变) repeatin ...

  5. cocoslua3.17 android机器上播放音效不全

    开发过程中遇到一个问题,一个8秒的音效,在android机器上播放不完就结束了:网上说是由于android播放音效的内存限制的:原因知道了,那怎么解决呢? 通过各种搜索查找发现还是解决不了问题,然后自 ...

  6. 一文搞懂所有Java集合面试题

    Java集合 刚刚经历过秋招,看了大量的面经,顺便将常见的Java集合常考知识点总结了一下,并根据被问到的频率大致做了一个标注.一颗星表示知识点需要了解,被问到的频率不高,面试时起码能说个差不多.两颗 ...

  7. 《图解TCP/IP》第四章

    <图解TCP/IP>第四章 4.1 IP 即网际协议 4.1.1 IP(IPv4.IPv6)相当于OSI参考模型中的第3层-网络层 4.1.2 数据链路层和网络层的关系: 数据链路层的主要 ...

  8. github搭建html网站到外网

    最近想自己弄个网站,但又没有服务器可以用,只好借用强大得github来帮忙了,不过GitHub确实有这个功能. 感谢以下大佬得教程,非常得详细,但我觉得还是有必要记录下来. 大佬链接: https:/ ...

  9. 【CF607B】Zuma——区间dp(记忆化搜索/递推)

    以下是从中文翻译成人话的题面: 给定一个长度小于等于500的序列,每个数字代表一个颜色,每次可以消掉一个回文串,问最多消几次可以消完? (7.16) 这个题从洛谷pend回来以后显示有103个测试点( ...

  10. C语言讲义——库函数排序qsort

    qsort函数在在stdlib.h中. 函数原型 void qsort(void *base, size_t nitems, size_t size, int (*compar)(const void ...