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

题目描述

n 桶水,第 i 桶水的体积是 ci。水桶的容量无限。可以进行不超过 LIM 次操作,每次选择 ij,cicj,使 ci,cjcicj,2cj。请将至少 n2 桶水倒空。输出方案。

QOJ1963:n=3,LIM=1000,ci109

BC2401B:n3×105,ci1010,LIM=8.5×105

solution n=3

不妨设 c1<c2<c3。我们提出以下算法,倒出一桶 c2modc1 的水:首先令 k=c2/c1

你观察,如果 c1 一直都是较小的数,那么 c1 会一直倍增。我们进行 1+log2k 次操作,第 j 次操作中,如果 k 的二进制第 j1 位为 1,则 c2c1 倒;否则 c3c1 倒。这样感性理解发现操作是合法的,且最后 c2 会变为 c2kc1,达到目的。

我们观察最小值,从 c1 变成了 c2modc1,它确实变小了,变成了 c2 的一半,但不一定是 c1 的一半。

我们提出以下算法,倒出一桶 c1(c2modc1)(k+1)c1c2 的水:将刚才的 k 改成 k+1,在最后一次操作中会变成 2lc1,c2(k+12l)c1,c3?c1lk+1 的最高位)。这时候显然有 2lc1c2(k+12l)c1,左边往右边倒水,左边就变成 (k+1)c1c2

既然可以使最小值从 c1 变为 c2modc1 或者 c1(c2modc1),此两者必有其一能折半,于是可以 O(log2ci) 解决原问题。

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

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

solution n<=3e5(题解)

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

对前一部分的次数分析:定义势能为 U=ilog2nm/lowbit(ci)mci 最大值,函数记为 lb(ci) 并定义 lb(0)=0)。初始时势能最大为 O(nlognm)。合并两桶水的时候,势能减少 O(dep) 意思是和他们在 Trie 树上的深度成线性。为分析次数的级别, 不妨假设所有的 dep=x,则最多有 2x 个点,次数为 nlognmxx 只能取 logn,分析出来 O(nlog(nm)/log(n))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 NF
  freopen("ball.in", "r", stdin);
  freopen("ball.out", "w", stdout);
#endif
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  atexit(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 NF
  freopen("ball.in", "r", stdin);
  freopen("ball.out", "w", stdout);
#endif
  cin.tie(nullptr)->sync_with_stdio(false);  
#endif
  atexit(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;
}
posted @   caijianhong  阅读(31)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· AI与.NET技术实操系列(五):向量存储与相似性搜索在 .NET 中的实现
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 上周热点回顾(3.3-3.9)
历史上的今天:
2023-09-25 题解 T378010 / SS241121C【Soso 的排列】
2023-09-25 题解 AtCoder Beginner Contest 268 A~H
点击右上角即可分享
微信分享提示