有一个数组 \(A\),可以在上面进行任意次操作。每次操作选择一个位置,把它减去 \(2^c\) 后再乘 \(2\)(其中 \(c\) 是已经在这个位置上操作过的操作数)。每次操作之后,数组中的所有元素必须是正整数。如果可以将它变得单调上升,那么它就是一个好的数组。
现在需要构造长为 \(n\) 的一个好的数组,使得 \(\sum A_i\) 最小,在此基础上使得它的字典序最小,输出 \(\sum A_i\),并 \(q\) 次询问某个位置上的值,保证位置升序给出。
\(n\le10^9,q\le\min(n,10^5)\)
第一道没看题解写出来的 CF 2000+?(但是队友也给了我很多提示,所以应该不算(
首先,我们考虑一个数经过若干次后可以变成什么,假设原来是 \(x\),那么经过 \(k\) 次操作后,\(x\) 会变成 \(2^k(x-k)\)。
接着,我们反过来考虑可以操作成 \(y\) 的数中最小的数是什么。
显然只要 \(2^k|y\),那么 \(\dfrac{y}{2^k}+k\) 就可以被操作成 \(y\)。
我们发现,只要 \(y\) 是一个偶数,那么有 \(y\ge2\),此时 \(y-\dfrac{y}{2}\ge1\),那么我们贪心地取尽可能大的 \(k\) 是不劣的。
因此,在下文中,我们定义 \(f(y)=\dfrac{y}{2^k}+k\),其中 \(k\) 是 \(y\) 二进制低位连续 \(0\) 的数量。
考虑回原问题,我们要操作出一个单调上升的 \(A\),这意味着 \(A\) 相邻两项必不能相同。
那么我们此时选择 \(f(A_i)\) 作为初始时原位置上的数必定是最优的。
因此我们可以选择考虑最终的序列 \(B\),答案就是 \(\sum f(B_i)\)。
题目中的要求是长至少为 \(n\),因此最优策略就是贪心地每次选择 \(f(x)\) 最小的还未出现过的 \(x\) 插入到 \(A\) 里。
接下来我们考虑对于一个数 \(x\),有多少个数 \(y\) 的 \(f(y)=x\),由于 \(f(y)=\dfrac{y}{2^k}+k\),我们令 \(z=\dfrac{y}{2^k}\)。
枚举 \(1\le z\le x\),假如 \(z\) 是一个偶数,那么不符合 \(k\) 是 \(y\) 二进制低位连续 \(0\) 的数量的定义,直接舍去。
剩余的所有奇数 \(z\) 都是合法的,因此我们可以得知,一共存在 \(\lceil\dfrac{x}{2}\rceil\) 个 \(y\) 使得 \(f(y)=x\)。
那么第一问就可以简单解决了,直接从小到大枚举 \(x\),最多只有 \(\sqrt{n}\) 个 \(x\)。
接下来我们考虑第二问,要求出所有数里面的第 \(k\) 大,第一眼看上去较困难,因为我们根本无法记下这么大和这么多的数。
但是我们发现每个数都能被表示为 \(2^ab\) 的形式,我们考虑对所有数取对数,那么这个数就可以被表示为 \(a+\log_2b\)。
\(\log_2b\) 是不好计算的,因为这个东西是小数,我们考虑枚举 \(b\),考虑所有的 \(a+log_2b\) 的数。
显然,对于所有 \(b \le x\) 的 \(x\) 都可以贡献到 \(b\),我们把 \(log_2b\) 的整数部分和小数部分分离,那么所有的 \(a+\log_2b\) 就可以被表示为 \(\log_2b\) 的小数部分再加上一个区间里整数的形式。
那么我们首先把所有 \(\log_2b\) 的小数部分预处理出来并离散化,然后从小到大对整数部分扫描线,然后用数据结构维护插入删除第 \(k\) 大即可。
我用的是树状数组,但是不知道为何仍然常数较大()
#pragma GCC optimize("Ofast", "inline", "no-stack-protector")
#include <algorithm>
#include <iostream>
#include <string.h>
#include <math.h>
#include <queue>
using std::cin, std::cout, std::cerr;
const int N = 1e6 + 7, Lg = std::__lg(N) + 1, M = 1 << Lg;
typedef long long i64;#define R(i,a,b) for(int i(a);i<=(b);++i)
typedef double unit;struct bit {int tr[M], cnt;void add(int x, int y) {cnt += y;for(; x < M; x += x & -x)tr[x] += y;}int qry(int x) {int y = 0;for(; x; x -= x & -x)y += tr[x];return y;}int kth(int k) {int p = M;for(int i = Lg-1; ~i; --i)if(tr[p - (1 << i)] >= k)p -= (1 << i);else k -= tr[p - (1 << i)];return p;}
} T;int n, q;unit val[N]; int offset[N], rnk[N], fr[N];
std::vector<unit> vals;
int qry[N], ans[N];
std::vector<int> st[N], ed[N];int main() {cin >> n >> q;int i = 1;i64 _ans = 0, t = 0;for(; t + (i + 1) / 2 < n; ++i) {t += (i + 1) / 2, _ans += i * ((i + 1) / 2);}_ans += i * (n - t);cout << _ans << "\n";for(int j = 1; j <= (i + 1) / 2; ++j) {int w = 2 * j - 1;val[j] = log2(w);while(val[j] >= 1) val[j] -= 1, ++offset[j];offset[j] -= w;vals.push_back(val[j]);}std::sort(vals.begin(), vals.end());vals.erase(std::unique(vals.begin(), vals.end()), vals.end());for(int j = 1; j <= (i + 1) / 2; ++j) {rnk[j] = std::lower_bound(vals.begin(), vals.end(), val[j]) - vals.begin() + 1;fr[rnk[j]] = j;}int Rp = 0;for(int j = 1; j <= (i + 1) / 2; ++j) {int l = 2 * j - 1, r = i - 1;if(j <= (n - t)) ++r;Rp = std::max(Rp, r + offset[j]);st[l+offset[j]].push_back(rnk[j]);ed[r+offset[j]+1].push_back(rnk[j]);}R(j, 1, q) cin >> qry[j];int Kt = 0, pt = 1;for(int j = 0; j <= Rp; ++j) {for(int& x: st[j]) T.add(x, 1);for(int& x: ed[j]) T.add(x, -1);int K = T.cnt;while(pt <= q && qry[pt] <= Kt + K) {int w = T.kth(qry[pt] - Kt);ans[pt++] = j - offset[fr[w]];}Kt += K;}R(j, 1, q) cout << ans[j] << "\n";
}