Codeforces Round #601 (Div. 2) A-E
A
题意
给两个数字 \(a,b\) ,每次操作可以使 \(a\) 加上 \(+1,+2,+5,-1,-2,-5\) 中的一个数,求最少多少次操作可以将 \(a\) 变成 \(b\) 。
题解
知识点:贪心。
可以贪心取,先 \(5\) 后 \(2\) 再 \(1\) 。
一点小结论(可能是假的qwq):
考虑三个硬币 \(a>b>c\) ,令 \(a = kb+mc,b = nc\) 。
若 \(n - m \leq k\) 则任意数量的 \(a\) 都不可替代。
若 \(n - i(m-1)-1 \leq ik,i \geq 1\) ,则一次不能替代大于等于 \(i\) 个 \(a\) ,但可以用 \(jk+1\) 个 \(b\) 替代 \(j\) 个 \(a\) 和 \(n-jm\) 个 \(c\) ,其中 \(j<i\) 。
时间复杂度 \(O(1)\)
空间复杂度 \(O(1)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
bool solve() {
int a, b;
cin >> a >> b;
int ans = abs(b - a) / 5 + abs(b - a) % 5 / 2 + abs(b - a) % 5 % 2;
cout << ans << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}
B
题意
有 \(n\) 个冰箱, \(m\) 个锁链,每个冰箱的价值为 \(a_i\) 。一个锁链可以连接两个冰箱,这两个冰箱就会被这条锁链锁住,而设置这条锁链的花费是连接的两个冰箱的价值之和。
要求你设置锁链使得每个冰箱至少被两条锁链锁住,并且花费最小。
题解
知识点:贪心,构造。
显然为了使得每个冰箱至少有两条锁链连着,那花费至少是编号总和乘 \(2\) ,考虑能否达成这个最小值。
显然,使用环形锁链结构,即可达成这个最小值,即 \(1 \to 2,2\to3,\cdots,n-1\to n,n \to 1\) ,而且用的锁链最少,为 \(n\) 条。
因此,若 \(n=2\) 或者 \(m<n\) 的情况,分别是不能构成环或锁链不够,那么是无解的。
时间复杂度 \(O(n)\)
空间复杂度 \(O(1)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
bool solve() {
int n, m;
cin >> n >> m;
int ans = 0;
for (int i = 1;i <= n;i++) {
int x;
cin >> x;
ans += x;
}
if (n == 2 || m < n) return false;
cout << 2 * ans << '\n';
for (int i = 1;i < n;i++) {
cout << i << ' ' << i + 1 << '\n';
}
cout << n << ' ' << 1 << '\n';
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}
C
题意
有一个长为 \(n\) 的排列 \(p\) ,生成一个长为 \(n-2\) 的三元组序列 \(q\) ,其中 \(q_i = (p_i,p_{i+1},p_{i+2})\) 。
现在给你被打乱的 \(q\) ,即每个三元组内部被打乱, \(q\) 的排序也被打乱,求符合 \(q\) 的一个 \(p\) 。
题解
知识点:构造。
注意到 \(p\) 生成的 \(q\) 中,数字出现的次数是有规律的。 \(p_1,p_n\) 恰好出现一次, \(p_2,p_{n-1}\) 恰好出现两次,其余元素都会恰好出现三次。通过这个性质我们就能初步断定出现一次的一定在首或尾,出现两次的一定在第二个或倒数第二个,其余元素不确定。
因此,我们先预处理所有数出现的次数,同时维护每个数与其他数是否在同一个元组的关系。之后,我们可以先取一个出现一次的数作为 \(p_1\) ,那么 \(p_1\) 所在元组就没用了,我们把与 \(p_1\) 在一个元组里的数的次数减一,此时出现新的出现次数为 \(1\) 的数,他就是紧接着的第二个数,以此类推取数即可。
但是有一个特例,最后 \(5\) 个数的时候呈现 \(1,2,3,2,1\) ,取走一个 \(1\) 的数以后呈现 \(1,2,2,1\) ,再取一次就变成 \(1,1,1\) ,出现了三个 \(1\) 没法判断了,因此,我们要在一开始确定 \(p_1\) 的时候,直接把 \(p_{n-1},p_{n}\) 都确定了,这样到最后三个不确定的数中已经有两个确定过了就可以直接判断了。于是,我们开一个数组 \(vis\) 记录数有没有被取过,一开始把 \(p_{n-1},p_n\) 取走就行。
时间复杂度 \(O(n)\)
空间复杂度 \(O(n)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int cnt[100007];
vector<int> g[100007];
bool vis[100007];
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
for (int i = 1;i <= n - 2;i++) {
int u, v, w;
cin >> u >> v >> w;
cnt[u]++;
cnt[v]++;
cnt[w]++;
g[u].push_back(v);
g[v].push_back(u);
g[u].push_back(w);
g[w].push_back(u);
g[v].push_back(w);
g[w].push_back(v);
}
vector<int> st;
for (int i = 1;i <= n;i++) {
if (cnt[i] == 1) st.push_back(i);
}
int fst = st[0];
int lst = st[1];
vis[fst] = vis[lst] = 1;
int lst2;
for (auto v : g[lst]) {
if (cnt[v] == 2) {
vis[v] = 1;
lst2 = v;
break;
}
}
for (int u = fst, w = 0;u;u = w) {
w = 0;
cout << u << ' ';
for (auto v : g[u]) {
if (vis[v]) continue;
cnt[v]--;
if (cnt[v] == 1) w = v;
}
}
cout << lst2 << ' ' << lst << '\n';
return 0;
}
D
题意
给一张 \(r \times c\) 的地图,地图上有米 R
和空地 .
。
现在有 \(k\) 只鸡,鸡的行走规则是只能从一格走到相邻的四格,地图上的格子鸡都能走。
现在让你给每只鸡分配 \(1\) 个区域,鸡只能在自己的区域里走,行走规则不变,要求每个鸡能吃到米的数量中的极差(最大值与最小值的差)最小。
题解
知识点:贪心,数学。
注意到,实际上我们可以给任意鸡分配任意的米数,只要我们走蛇形分配区域,就能保证区域一定是连通的。
设总米数为 \(sum\) ,考虑给每只鸡先分配 \(\left\lfloor \dfrac{sum}{k} \right\rfloor\) 个米,多出来的 \(sum \bmod k\) 个米一人一个分配完,这样极差最小。
时间复杂度 \(O(rc)\)
空间复杂度 \(O(rc)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
char dt[107][107];
bool solve() {
int r, c, k;
cin >> r >> c >> k;
int sum = 0;
for (int i = 1;i <= r;i++) {
for (int j = 1;j <= c;j++) {
cin >> dt[i][j];
if (dt[i][j] == 'R') sum++;
}
}
int avg = sum / k;
int rst = sum % k;
int pos = 0, cnt = 0;
for (int i = 1;i <= r;i++) {
if (i & 1) {
for (int j = 1;j <= c;j++) {
if (dt[i][j] == 'R') cnt++;
if (cnt > avg + (rst > 0)) {
cnt = 1;
pos++;
if (rst > 0) rst--;
}
if (pos < 10) dt[i][j] = pos + '0';
else if (10 <= pos && pos < 36) dt[i][j] = pos - 10 + 'A';
else dt[i][j] = pos - 36 + 'a';
}
}
else {
for (int j = c;j >= 1;j--) {
if (dt[i][j] == 'R') cnt++;
if (cnt > avg + (rst > 0)) {
cnt = 1;
pos++;
if (rst > 0) rst--;
}
if (pos < 10) dt[i][j] = pos + '0';
else if (10 <= pos && pos < 36) dt[i][j] = pos - 10 + 'A';
else dt[i][j] = pos - 36 + 'a';
}
}
}
for (int i = 1;i <= r;i++) {
for (int j = 1;j <= c;j++) {
cout << dt[i][j];
}
cout << '\n';
}
return true;
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
if (!solve()) cout << -1 << '\n';
}
return 0;
}
E
题意
给 \(n\) 个数 \(a_i\) ,每次操作可以任选一个数 \(a_i\) 减 \(1\) 并在 \(a_{i-1},a_{i+1}\) 中选择一个加 \(1\) (如果存在的话),即 \(a_i\) 给它的一个邻居一个 \(1\) 。
要求使用最少的操作次数,使得存在一个 \(k>1\) 能整除所有数。
Easy版 \(n \in [1,10^5],a_i \in \{ 0,1\}\)
Hard版 \(n \in [1,10^6],a_i \in [1,10^6]\)
题解
知识点:质因数分解,贪心,枚举。
如果最后 \(k\) 能整除 \(a_i\) ,等价于 \(k\) 能整除 \(sum = \sum a_i\) ,而总和是不变的,因此我们可以直接通过 \(sum\) 的因子求出 \(k\) 的可行值来枚举。
显然,对于 \(10^{12}\) 的数,枚举他的全部因子的复杂度是不可行的。实际上,并不是所有 \(k\) 都需要枚举的,我们只需要枚举 \(sum\) 的质因子 \(k\) 即可,因为对于一个合数因子的答案,他的质因子的答案不会更坏。质因子最多 \(12\) 个,复杂度完全可以接受。所以,我们先处理出 \(sum\) 的质因子 \(pfactor\) 数组,随后遍历求出操作取最小值。
对于一个确定的 \(k\) ,我们希望得到修改 \(a_i\) 数组的最小操作次数。我们可以从左往右遍历,假设前 \(i-1\) 个都已经能被整除,那么 \(a_i\) 没有必要对 \(a_{i-1}\) 操作了, 只可能把 \(a_i \bmod k\) 个 \(1\) 给 \(a_{i+1}\) 或者让 \(a_{i+1}\) 给自己 \(k - a_i \bmod k\) 个 \(1\) ,从中取最小值就是 \(a_i\) 需要的操作。
注意,前 \(i-1\) 能被 \(k\) 整除后,前 \(i-1\) 个数的操作最后都积累到 \(i\) 身上了,因此对于 \(a_i\) ,它已经变成 \((\sum_{j=1}^i a_i) \bmod k\) 。因此,令 \((\sum_{j=1}^i a_i) \bmod k = pre_i\) ,我们每次取最小值时,实际上是取 \(\min (pre_i,k-pre_i)\) 。
特判 \(sum = 1\) 的情况无解。
对于easy版:
我们可以直接枚举因子 \(k\) ,总数是 \(100\) 量级。
我们对一个 \(k\) 求最小操作时,可以直接贪心地每 \(k\) 个 \(1\) 分区域,每个区域的操作最小值是 \(k\) 个数到第 \(\left\lceil \dfrac{k}{2}\right\rceil\) 个数的距离总和(因为 \(a_i\) 都是 \(1\) ,代价等价于距离)。证明方法:可以设 \(f(x)\) 为选择到第 \(x\) 个数的距离总和,通过作差 \(f(x+1)-f(x)\) 发现是一个单谷函数,极值点在 \(x = \left\lceil \dfrac{k}{2}\right\rceil\) 。
时间复杂度 \(O(n+\sqrt{\sum a_i})\)
空间复杂度 \(O(n)\)
代码
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
int a[1000007];
void get_pfactor(ll n, vector<ll> &pfactor) {
for (int i = 2;1LL * i * i <= n;i++) {
if (n % i == 0) {
pfactor.push_back(i);
while (n % i == 0) n /= i;
}
}
if (n > 1) pfactor.push_back(n);
}
int main() {
std::ios::sync_with_stdio(0), cin.tie(0), cout.tie(0);
int n;
cin >> n;
ll sum = 0;
for (int i = 1;i <= n;i++) cin >> a[i], sum += a[i];
if (sum == 1) {
cout << -1 << '\n';
return 0;
}
vector<ll> pfactor;
get_pfactor(sum, pfactor);
ll ans = 1e18;
for (auto p : pfactor) {
ll pre = 0, cnt = 0;
for (int i = 1;i <= n;i++) {
(pre += a[i]) %= p;
cnt += min(pre, p - pre);
}
ans = min(ans, cnt);
}
cout << ans << '\n';
return 0;
}
Codeforces Round #601 (Div. 2) A-E的更多相关文章
- 【cf比赛记录】Codeforces Round #601 (Div. 2)
Codeforces Round #601 (Div. 2) ---- 比赛传送门 周二晚因为身体不适鸽了,补题补题 A // http://codeforces.com/contest/1255/p ...
- Codeforces Round #601 (Div. 2)
传送门 A. Changing Volume 签到. Code /* * Author: heyuhhh * Created Time: 2019/11/19 22:37:33 */ #include ...
- Codeforces Round #601 (Div. 2) E2. Send Boxes to Alice (Hard Version)
Codeforces Round #601 (Div. 2) E2. Send Boxes to Alice (Hard Version) N个盒子,每个盒子有a[i]块巧克力,每次操作可以将盒子中的 ...
- Codeforces Round #601 (Div. 2) E1 Send Boxes to Alice (Easy Version)
#include <bits/stdc++.h> using namespace std; typedef long long ll; ; int a[N]; int n; bool pr ...
- Codeforces Round #601 (Div. 2) D Feeding Chicken
//为了连贯,采取一条路形式,从第一行开始 也就是s型 #include <bits/stdc++.h> using namespace std; ; char str[MAXN][MAX ...
- Codeforces Round #601 (Div. 2) C League of Leesins
把每一次输入的一组数字存下来,然后把每个数字出现的组数存下来 然后找只出现过一次的数字a,那么这个数字a不是开头就是结尾,默认为开头(是哪个都无所谓),然后去找和它出现在同一组的两个数字b和c,而b和 ...
- Codeforces Round #601 (Div. 2) B Fridge Lockers
//题目要求的是每一个点最少要有两条边连接,所以可以先构成一个环.然后再把剩余的最短的边连接起来 #include<iostream> #include<algorithm> ...
- Codeforces Round #601 (Div. 2) A Changing Volume
好吧,其实我拿到这个题的时候,首先想到了bfs,写完之后,开开森森的去交代码,却在第二个数据就TEL,然后优化半天,还是不行. 最终,我盯着1,2,5发呆半天,wc,然后直接贪心 #include&l ...
- Codeforces Round #601 (Div. 2)E(寻找质因子,DP)
先分解质因数,对于当前a[i],假设当前的质因数为x,这个位置要满足能被k整除,有两个可能,要么是它向后一个转移x%k个,要么是后一个向它转移k-x%k个. 对于每一个a[i]满足后,因为只会对下一个 ...
- Codeforces Round #601 (Div. 2)D(蛇形模拟)
#define HAVE_STRUCT_TIMESPEC #include<bits/stdc++.h> using namespace std; vector<char>an ...
随机推荐
- 【JavaSE】面向对象三大特征——封装、继承、多态
前言:本文主要介绍思想 封装 封装这一概念并不仅存在与面向对象中,甚至说封装这一概念不仅限于编程中,其实生活中的封装无处不在.比如 需求:你到银行取钱 参数:你只需要提供银行卡和密码 返回值:柜员会将 ...
- JS逆向实战8——某网实战(基于golang-colly)
其实本章算不上逆向教程 只是介绍golang的colly框架而已 列表页分析 根据关键字搜索 通过抓包分析可知 下一页所请求的参数如下 上图标红的代表所需参数 所以其实我们真正需要的也就是Search ...
- Linux---ls cd
ls 命令 ls命令是linux下最常用的命令,是 list 的缩写,可以用各种方式查看目录中的内容. 格式: ls [选项] [目录名] 常用参数 short long function -a -- ...
- Nginx负载均衡策略的介绍与调优
工作中经常会用到nginx负载均衡这一块,下面对nginx负载均衡策略做个总结.本人在工作中最常用到的负载均衡策略是轮询策略. 在一般情况下,Web中间件最大的作用就是负责对请求进行分发,也就是我们常 ...
- mycat搭建
搭建mycat 一.准备工作 1.确保jdk已安装成功,并且jdk版本选用1.7以上版本 2.准备一台新的主机mysql_mycat放到master的前面做代理 mycat ip 192.168.23 ...
- networkQuality
基本使用 networkQuality 是一个命令行工具,需要使用「终端」App(或者你首选的其他终端模拟器)运行.方法是: 首先,点按「程序坞」(Dock)中的「启动台」(LaunchPad)图标, ...
- 【DL论文精读笔记】VGGNet
VGGNet(Very Deep Convolutional Networks) 1.introduction ● 采用3x3的小卷积核应用在比较深的网络里 ● 结果不错,赢得了2014 Imagen ...
- easui datagrid 行获取后台sql所有数据:支持行chockbox多选,输出选中行任意属性;支持点击表中属性实现跳转;支持分页。
easyUI datagrid 代码: <table id="tabgrid20170726191838251403" class="easyui-datagrid ...
- 各类数据库写入Webhsell总结
1.MySQL写入WebShell 1.1写入条件 数据库的当前用户为ROOT或拥有FILE权限: 知道网站目录的绝对路径: PHP的GPC参数为off状态: MySQL中的secure_file_p ...
- java (String)强制转换与toString()方法
1. Object.toString()介绍 Object中是自带有toString()方法的,也就是说java中的所有类的对象都是可以转换为字符串的. 首先,先看看Object.toString() ...