题解 QOJ1963【[Seoul21J] Squid Game】/ BC2401B【空当接球】

news/2024/9/25 22:06:05

题目描述

\(n\) 桶水,第 \(i\) 桶水的体积是 \(c_i\)。水桶的容量无限。可以进行不超过 \(LIM\) 次操作,每次选择 \(i\neq j ,c_i\geq c_j\),使 \(c_i', c_j'\gets c_i-c_j, 2c_j\)。请将至少 \(n-2\) 桶水倒空。输出方案。

QOJ1963:\(n=3, LIM=1000, c_i\leq 10^9\)

BC2401B:\(n\leq 3\times 10^5, c_i\leq 10^{10}, LIM=8.5\times 10^5\)

solution n=3

不妨设 \(c_1<c_2<c_3\)。我们提出以下算法,倒出一桶 \(c_2\bmod c_1\) 的水:首先令 \(k=\left\lfloor c_2/c_1\right\rfloor\)

你观察,如果 \(c_1\) 一直都是较小的数,那么 \(c_1\) 会一直倍增。我们进行 \(1+\log_2 k\) 次操作,第 \(j\) 次操作中,如果 \(k\) 的二进制第 \(j-1\) 位为 \(1\),则 \(c_2\)\(c_1\) 倒;否则 \(c_3\)\(c_1\) 倒。这样感性理解发现操作是合法的,且最后 \(c_2\) 会变为 \(c_2-kc_1\),达到目的。

我们观察最小值,从 \(c_1\) 变成了 \(c_2\bmod c_1\),它确实变小了,变成了 \(c_2\) 的一半,但不一定是 \(c_1\) 的一半。

我们提出以下算法,倒出一桶 \(c_1-(c_2\bmod c_1)\)\((k+1)c_1-c_2\) 的水:将刚才的 \(k\) 改成 \(k+1\),在最后一次操作中会变成 \(2^lc_1, c_2-(k+1-2^l)c_1, c_3-?c_1\)\(l\)\(k+1\) 的最高位)。这时候显然有 \(2^lc_1\geq c_2-(k+1-2^l)c_1\),左边往右边倒水,左边就变成 \((k+1)c_1-c_2\)

既然可以使最小值从 \(c_1\) 变为 \(c_2\bmod c_1\) 或者 \(c_1-(c_2\bmod c_1)\),此两者必有其一能折半,于是可以 \(O(\log^2c_i)\) 解决原问题。

solution n<=3e5(黄队做法)

将序列从小到大排序,如果相邻两项的商比较小,小到 \(1+\epsilon\),那么对相邻两项倒一次水,会使得其中一项变的比较小,方便后续的处理。根据黄队所说,取 \(\epsilon=0.6\),对整个序列跑 \(50\) 次,有很不错的效果。

solution n<=3e5(题解)

从小到大枚举 \(\text{lowbit}(c_i)\),将所有这样的 \(c_i\) 放在一起。这时候,随意抽取两个 \(c_i\),对他们倒水,显然小的那桶水 \(\text{lowbit}\)\(2\),另一桶 \(\text{lowbit}\) 至少乘 \(2\)(甚至可能会起飞)。我们搞一个 Trie 树,从低位到高位插入这些 \(c_i\),然后在上面 dfs,从下到上进行合并,尽可能使它的 \(\text{lowbit}\) 飞的更远。这样就能把所有水桶倒到只剩 \(O(\log nc_i)\)\(\text{lowbit}\) 互不相同的水,再逐对跑 \(n=3\) 的算法就是 \(O(\log^3nc_i)\)

对前一部分的次数分析:定义势能为 \(U=\sum_i\log_2nm/\text{lowbit}(c_i)\)\(m\)\(c_i\) 最大值,函数记为 \(lb(c_i)\) 并定义 \(lb(0)=0\))。初始时势能最大为 \(O(n\log nm)\)。合并两桶水的时候,势能减少 \(O(dep)\) 意思是和他们在 Trie 树上的深度成线性。为分析次数的级别, 不妨假设所有的 \(dep=x\),则最多有 \(2^x\) 个点,次数为 \(\frac{n\log nm}{x}\)\(x\) 只能取 \(\log n\),分析出来 \(O(n\log(nm)/\log(n))\approx O(n)\) 的次数(好草率的分析啊。。。但是没办法,根本不可能有数据卡满)。

code

黄队做法实现
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
mt19937 rng{random_device{}()};
struct node {LL x;int id;operator LL() const { return x; }
};
vector<pair<int, int>> ans;
void perf(node& lhs, node& rhs) {if (lhs < rhs) swap(lhs, rhs);assert(lhs.id != rhs.id);lhs.x -= rhs.x, rhs.x <<= 1;ans.emplace_back(lhs.id, rhs.id);if (ans.size() >= (int)8.5e5) exit(0);
}
void printans() {cout << ans.size() << endl;for (auto&& e : ans) cout << e.first << " " << e.second << endl;
}
int n;
LL m;
void solve(vector<node> vec) {shuffle(vec.begin(), vec.end(), rng);auto choice = [&]() {swap(vec[rng() % vec.size()], vec.back());auto ret = vec.back();vec.pop_back();return ret;};while (vec.size() > 2) {vector<node> tmp{choice(), choice(), choice()};auto &a = tmp[0], &b = tmp[1], &c = tmp[2];while (a && b && c) {sort(tmp.begin(), tmp.end());if (b == c) {perf(b, c);} else if (a) {auto k = b / a + (b % a > a / 2);for (LL j = 1; j <= k; j <<= 1) perf(j & k ? b : c, a);}}for (auto e : {a, b, c}) if (e) vec.push_back(e);}
}
node a[300010];
int main() {
#ifndef LOCAL
#ifndef NFfreopen("ball.in", "r", stdin);freopen("ball.out", "w", stdout);
#endifcin.tie(nullptr)->sync_with_stdio(false);  
#endifatexit(printans);cin >> n >> m;for (int i = 1; i <= n; i++) cin >> a[i].x, a[i].id = i;for (int t = 1; t <= 200; t++) {sort(a + 1, a + n + 1);for (int i = 1; i < n; i++) if (a[i] && (double)a[i + 1] / a[i] < 1.6) perf(a[i + 1], a[i]);}solve(vector<node>(a + 1, a + n + 1));return 0;
}
题解做法实现
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
mt19937 rng{random_device{}()};
struct node {LL x;int id;operator LL() const { return x; }
};
vector<pair<int, int>> ans;
void perf(node& lhs, node& rhs) {if (lhs < rhs) swap(lhs, rhs);assert(lhs.id != rhs.id);lhs.x -= rhs.x, rhs.x <<= 1;ans.emplace_back(lhs.id, rhs.id);if (ans.size() >= (int)8.5e5) exit(0);
}
void printans() {cout << ans.size() << endl;for (auto&& e : ans) cout << e.first << " " << e.second << endl;
}
int n;
LL m;
void solve(vector<node> vec) {shuffle(vec.begin(), vec.end(), rng);auto choice = [&]() {swap(vec[rng() % vec.size()], vec.back());auto ret = vec.back();vec.pop_back();return ret;};while (vec.size() > 2) {vector<node> tmp{choice(), choice(), choice()};auto &a = tmp[0], &b = tmp[1], &c = tmp[2];while (a && b && c) {sort(tmp.begin(), tmp.end());if (b == c) {perf(b, c);} else if (a) {auto k = b / a;for (LL j = 1; j <= k; j <<= 1) perf(j & k ? b : c, a);}}for (auto e : {a, b, c}) if (e) vec.push_back(e);}
}
node a[300010];
int main() {
#ifndef LOCAL
#ifndef NFfreopen("ball.in", "r", stdin);freopen("ball.out", "w", stdout);
#endifcin.tie(nullptr)->sync_with_stdio(false);  
#endifatexit(printans);cin >> n >> m;for (int i = 1; i <= n; i++) cin >> a[i].x, a[i].id = i;for (int t = 1; t <= 200; t++) {sort(a + 1, a + n + 1);for (int i = 1; i < n; i++) if (a[i] && (double)a[i + 1] / a[i] < 1.6) perf(a[i + 1], a[i]);}solve(vector<node>(a + 1, a + n + 1));return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.ryyt.cn/news/64760.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

9-12

9段好的,我会逐句翻译并解释其中的关键词汇及其发音。 1. **There are, of course, people belonging to all classes who do not want to be fascinated and then enslaved by Admass, and who if necessary are ready to make a few sacrifices, largely material, to achie…

“人民冻凉”简介

账号定位: 这是一个由 复旦大学 的学生运营的 非官方自媒体账号 。 它最大的标签就是 复旦。 其次是复旦附带的的 \(985\)、江浙沪、上海交大、清北华五 这类的 \(\text{tag}\) 。 可以简单理解为,这是一个上海版的 全元光滑 。但实际上,考虑到两者在 学校的地域、创始团队的…

02 深浅拷贝关于 str int bool

深浅拷贝 list /set /dict 一层

河道污染物识别系统

河道污染物识别系统通过深度学习技术,河道污染物识别系统对监控画面中河道污染物以及漂浮物进行全天候实时监测,当河道污染物识别系统监测到河道水面出现污染物时,立即抓拍存档触发告警并同步通知相关人员及时处理。河道污染物识别系统利用河道两旁现场摄像头可及时发现河道…

05 字典内存分配

data_list = [] for i in range(10):data = {}data[user] = idata_list.append(data) print(data_list) #每个字典都 不一样字典,列表内存指向图 data = {} for i in range(10):data[user] = i print(data)内存占用图

00 内存分配 -- 重点

要确认是进行赋值,还是找到其中, 有赋值为:重新开辟内存空间 python 将:-5~ 256为常用的数字(如果在范围类使用同一内存空间,这叫:python小数据池) 如果大于这个数值,会重新 进行开僻内存空间 字符串:如果A1 = ‘’alex A2= ‘alex , A1/A2等于同一个字符串 ,理应不…

01 内存地址 示例

示例一: v1 = [11,22,33] v2 = [11,22,33] v1 = 666 v2 = 666v1 = "asdf" v2 = "asdf"#以上数据都不是同一个内存地址# 按理 v1 和 v2 应该是不同的内存地址。特殊: 1. 整型: -5 ~ 256 2. 字符串:"alex",asfasd asdf asdf d_asdf …