题目链接

本蒟蒻的第一篇题解,写得不好请指出,敬请谅解

题意:

有\(n\)头奶牛,分布在一些房间,某些房间可能有多头牛,要让这些牛按顺时针移动,求使每一个房间刚好有一个奶牛的最小花费

花费计算:如果一头奶牛穿过了\(d\)扇门,他消耗的能量为\(d^2\)

分析:

先把这个环看成一条链

首先说一个东西:如果有\(1\)头奶牛在\(a\)点,\(1\)头奶牛在\(b\)点,还有一个没有奶牛的\(c\)点,且\(c>b>a\),要想有一头奶牛在\(b\)点,一头奶牛在\(c\)点,方案\(a \to b,b \to c\)比方案\(a \to c\)好

很容易知道这是对的,可以设\(x=b-a,y=c-b\),而\(c-a=y+x\),所以\((x+y)^2=x^2+y^2+2xy>x^2+y^2\)

也就是说在如果一个房间的奶牛的移动算一个移动过程,移动过程中一头奶牛不要越过另一头没有移动的奶牛

所以可以直接从一个起点开始,如果这个房间里有超过\(1\)头奶牛,就把这些奶牛移到依次后面每一个的房间的末尾,直到没有多余的奶牛可以移动为止

留下房间里最后一头奶牛,就是上一次最后一头牛,每一个房间都这么处理

为了好写代码,可以留下最后一头牛,把这个房间里多余的奶牛移动到下一个房间,下一个房间再做处理

问题是起点如何选择

当一个起点是合法时,\(\sum_{i=1}^{n}c_{i} \geqslant i\)

不知道就枚举呗,反正\(n\)不超过\(1000\)

窝语文不好,只能描述成这样惹

代码:

为每一个奶牛编号,为\(1 \sim n\)

用\(d_i\)来表示编号为\(i\)的奶牛走过的距离

用\(vector\)来存每一个房间里的奶牛

因为\(push\_back\)是把元素\(push\)到末尾,所以留下最后一个,其他的往下一个房间移动(还是上面那个东西)

然后注意下一个房间\(%n\)就行了

#include <bits/stdc++.h>
using namespace std;
#define mp make_pair
#define reg register int
#define msz(a) memset(a, 0, sizeof a);
#define rep1(i, j, n) for (reg i = j; i <= n; ++i)
#define rep2(i, j, n) for (reg i = j; i >= n; --i)
typedef long long LL;
typedef pair <int, int> PII;
const int INF1 = 0x3f3f3f3f, INF2 = 0x7fffffff;
int n, d[1005], ans, nowid, result = INF2;
vector <int> a[1005], b[1005];
bool vis[1005];
int main() {
cin >> n;
for (int i = 1; i <= n; ++i) {
int x;
cin >> x;
for (int j = 1; j <= x; ++j) a[i].push_back(++nowid), b[i].push_back(nowid);
}
for (int start = 1; start <= n; ++start) {
bool flg = 1;
for (int now = start; ; ++now) {
if (now > n) now %= n;
if (now == start && flg == 0) break;
if (a[now].empty()) continue;
flg = 0;
int sz = a[now].size();
int p = now + 1;
if (p > n) p %= n;
for (int i = 0; i < sz - 1; ++i) {
a[p].push_back(a[now][i]);
d[a[now][i]]++;
}
int endnum = a[now][sz - 1];
a[now].clear();
a[now].push_back(endnum);
}
for (int i = 1; i <= n; ++i) {
if (a[i].size() != 1) { ans = -1; break; }
ans += d[i] * d[i];
}
if (ans ^ -1) result = min(result, ans);
msz(d); msz(vis); ans = 0;
for (int i = 1; i <= n; ++i) a[i].clear();
for (int i = 1; i <= n; ++i)
for (int j = 0; j < (int)b[i].size(); ++j)
a[i].push_back(b[i][j]);
}
printf("%d", result);
return 0;
}

\(n^2\)算法可以通过\(1000\)的数据,那么\(100000\)的数据怎么办呢

可以发现枚举\(start\)会出现许多没用的枚举,那哪个一个\(start\)可以确保不会出现非法情况呢

可以大概猜出是最密集的那个地方的开头吧

就是说最大字段和

因为全部奶牛的和为\(n\),所以所有\(c_i\)的和一定是最大子段和

所以要把每一个\(c_i\)与\(1\)做差,可以清晰看出最大的字段,再求出最大子段和的起点

证明也很简单:

设最大子段和的值为\(x\),如果最大子段和不合法,那么在最大子段和之后必然有一个字段的值 \(\leqslant -x-1\),由于所有数的和为\(0\),那么在这个字段之后,剩下的那个字段和\(\geqslant 1\)

所以最大子段和的值不为\(x\),矛盾

这个证明有点。。充分显示出我的菜

然后就是环状最大子段和惹

环状最大子段和好像可以用单调队列,但是我用了另一种方法

分两种情况讨论:

\(1\).这个字段没有包含\(c_1\)和\(c_n\),那么就是本来的最大子段和

\(2\).字段包含了\(c_1\)和\(c_n\),那就是整个和减去全部的最小字段和

下面是代码:

#include <bits/stdc++.h>
using namespace std;
#define int long long
#define mp make_pair
#define reg register int
#define msz(a) memset(a, 0, sizeof a);
#define rep1(i, j, n) for (reg i = j; i <= n; ++i)
#define rep2(i, j, n) for (reg i = j; i >= n; --i)
typedef long long LL;
typedef pair <int, int> PII;
const int INF1 = 0x3f3f3f3f, INF2 = 0x7fffffff;
const int N = 1e5 + 5;
int n, d[N], ans, nowid, result = INF2, maxn, start, f[N], b[N];
vector <int> a[N];
bool vis[N];
signed main() {
// freopen("P6170_7.in", "r", stdin);
scanf("%lld", &n);
for (int i = 1; i <= n; ++i) {
int x;
scanf("%lld", &x);
b[i] = x - 1;
for (int j = 1; j <= x; ++j) a[i].push_back(++nowid);
}
bool flg = 1;
int m1 = 0, m2 = 0, sum = 0, bg1, st1, st2;
for (int i = 1; i <= n; ++i) {
if (f[i - 1] > 0) f[i] = f[i - 1] + b[i];
else f[i] = b[i], bg1 = i;
if (f[i] > m1) m1 = f[i], st1 = bg1;
sum += b[i];
b[i] = -b[i];
}
for (int i = 1; i <= n; ++i) {
if (f[i - 1] > 0) f[i] = f[i - 1] + b[i];
else f[i] = b[i];
if (f[i] > m2) m2 = f[i], st2 = i + 1;
}
m2 = sum + m2;
if (m1 > m2) start = st1;
else start = st2;
for (int now = start; ; ++now) {
if (now > n) now %= n;
if (now == start && flg == 0) break;
if (a[now].empty()) continue;
flg = 0;
int sz = a[now].size();
int p = now + 1;
if (p > n) p %= n;
for (int i = 0; i < sz - 1; ++i) {
a[p].push_back(a[now][i]);
d[a[now][i]]++;
}
int endnum = a[now][sz - 1];
a[now].clear();
a[now].push_back(endnum);
}
for (int i = 1; i <= n; ++i)
ans += d[i] * d[i];
printf("%lld", ans);
return 0;
}

洛谷 P3137 [USACO16FEB]Circular Barn S的更多相关文章

  1. 洛谷 P3137 [USACO16FEB]圆形谷仓Circular Barn_Silver

    P3137 [USACO16FEB]圆形谷仓Circular Barn_Silver 题目描述 Being a fan of contemporary architecture, Farmer Joh ...

  2. 洛谷P3138 [USACO16FEB]负载平衡Load Balancing_Silver

    P3138 [USACO16FEB]负载平衡Load Balancing_Silver 题目描述 Farmer John's NN cows are each standing at distinct ...

  3. 洛谷 P2701 [USACO5.3]巨大的牛棚Big Barn Label:二维数组前缀和 你够了 这次我用DP

    题目背景 (USACO 5.3.4) 题目描述 农夫约翰想要在他的正方形农场上建造一座正方形大牛棚.他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方.我们假定,他的农场划分成 N ...

  4. USACO Section 1.3 题解 (洛谷OJ P1209 P1444 P3650 P2693)

    usaco ch1.4 sort(d , d + c, [](int a, int b) -> bool { return a > b; }); 生成与过滤 generator&& ...

  5. 洛谷P2982 [USACO10FEB]慢下来Slowing down(线段树 DFS序 区间增减 单点查询)

    To 洛谷.2982 慢下来Slowing down 题目描述 Every day each of Farmer John's N (1 <= N <= 100,000) cows con ...

  6. 洛谷 P2986 [USACO10MAR]伟大的奶牛聚集Great Cow Gat…(树规)

    题目描述 Bessie is planning the annual Great Cow Gathering for cows all across the country and, of cours ...

  7. 洛谷 P3128 [USACO15DEC]最大流Max Flow-树上差分(点权/点覆盖)(模板题)

    因为徐州现场赛的G是树上差分+组合数学,但是比赛的时候没有写出来(自闭),背锅. 会差分数组但是不会树上差分,然后就学了一下. 看了一些东西之后,对树上差分写一点个人的理解: 首先要知道在树上,两点之 ...

  8. 洛谷1640 bzoj1854游戏 匈牙利就是又短又快

    bzoj炸了,靠离线版题目做了两道(过过样例什么的还是轻松的)但是交不了,正巧洛谷有个"大牛分站",就转回洛谷做题了 水题先行,一道傻逼匈牙利 其实本来的思路是搜索然后发现写出来类 ...

  9. 洛谷P1352 codevs1380 没有上司的舞会——S.B.S.

    没有上司的舞会  时间限制: 1 s  空间限制: 128000 KB  题目等级 : 钻石 Diamond       题目描述 Description Ural大学有N个职员,编号为1~N.他们有 ...

  10. 洛谷P1108 低价购买[DP | LIS方案数]

    题目描述 “低价购买”这条建议是在奶牛股票市场取得成功的一半规则.要想被认为是伟大的投资者,你必须遵循以下的问题建议:“低价购买:再低价购买”.每次你购买一支股票,你必须用低于你上次购买它的价格购买它 ...

随机推荐

  1. JIRA操作之JQL

    搜索功能 Jira的搜索功能非常强大,有专用的搜索语言JQL(Jira Query Language).Jira的Python库是基于JQL语法搜索的,返回的是搜索到的问题列表. jira.searc ...

  2. Git安装与常用操作

    Git作为一个版本控制工具,使用前需进行下载安装:可自行到官网下载. 一.安装(windows) 1.双击下载好的文件进行安装,弹窗中点击"next" 2.默认勾选,继续点击&qu ...

  3. nsenter命令简介

    nsenter命令是一个可以在指定进程的命令空间下运行指定程序的命令.它位于util-linux包中. 用途 一个最典型的用途就是进入容器的网络命令空间.相当多的容器为了轻量级,是不包含较为基础的命令 ...

  4. RSA、DSA 和 ECC 加密算法有什么区别?

    RSA.DSA 和 ECC 加密算法是用于在公钥基础设施中生成密钥的主要算法. 公钥基础设施 (PKI) 用于管理互联网通信和计算机网络中的身份和安全性. 启用 PKI 的核心技术是公钥密码术,这是一 ...

  5. 云原生之旅 - 13)基于 Github Action 的自动化流水线

    前言 GItHub Actions是一个持续集成和持续交付的平台,能够让你自动化你的编译.测试和部署流程.GitHub 提供 Linux.Windows 和 macOS 虚拟机来运行您的工作流程,或者 ...

  6. POST请求发送的表单数据和json数据的区别及python代码实现

    前言 这篇博客会介绍最常见post 请求form表单数据和json数据 数据类型之间的区别, urllib代码的实现(python), requests库实现, 以及如何使用postman软件发送这些 ...

  7. day16 异常处理生成器

    day16 异常处理生成器 今日内容概要 异常处理 异常处理实战应用 生成器对象 生成器对象实现range方法 生成器表达式 今日内容详细 一.异常处理 1.异常常见类型 SyntaxError语法错 ...

  8. Spring Boot+Mybatis:实现数据库登录注册与两种properties配置参数读取

    〇.参考资料 1.hutool介绍 https://blog.csdn.net/abst122/article/details/124091375 2.Spring Boot+Mybatis实现登录注 ...

  9. 【每日一题】【找到位置返回&升序数组中第K大就是n-K小】2022年1月17日-NC88 寻找第K大

    描述有一个整数数组,请你根据快速排序的思路,找出数组中第 k 大的数. 给定一个整数数组 a ,同时给定它的大小n和要找的 k ,请返回第 k 大的数(包括重复的元素,不用去重),保证答案存在. 方法 ...

  10. 【离线数仓】Day04-即席查询(Ad Hoc):Presto链接不同数据源查询、Druid建多维表、Kylin使用cube快速查询

    一.Presto 1.简介 概念:大数据量.秒级.分布式SQL查询engine[解析SQL但不是数据库] 架构 不同worker对应不同的数据源(各数据源有对应的connector连接适配器) 优缺点 ...