题解 CF1810H Last Number
kyEEcccccc · · 题解
大难题,但是非常的有意思。思路来自 艾利克斯·伟。补充了一点小细节。
题意
对于一个 可重 集合
给定
做法
首先观察题目给出的操作序列,容易发现这些操作是分为两部分的。记第
这部分先按下不表,转而考察
接下来,考虑前半部分操作。首先,
你可以把生成
那么我们就获得了一种优雅的方法生成
接下来,只需要结合上面对于后半部分操作的结论即可。容易得出,考虑我们在第
那么我们要支持快速求
代码
// Author: kyEEcccccc
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using ULL = unsigned long long;
#define F(i, l, r) for (int i = (l); i <= (r); ++i)
#define FF(i, r, l) for (int i = (r); i >= (l); --i)
#define MAX(a, b) ((a) = max(a, b))
#define MIN(a, b) ((a) = min(a, b))
#define SZ(a) ((int)((a).size()) - 1)
constexpr int K = 23;
struct SegInfo
{
int len, odd, even, sum;
SegInfo operator +(const SegInfo& o)
const {
if (len % 2 == 0) return {len + o.len, odd + o.odd, even + o.even, sum + o.sum};
else return {len + o.len, odd + o.even, even + o.odd, sum + o.sum};
}
};
SegInfo f[2][K];
void init(void)
{
f[0][0] = {1, 0, 0, 0};
f[1][0] = {1, 1, 0, 1};
F(i, 1, K - 1)
{
f[0][i] = f[1][i - 1] + f[0][i - 1];
f[1][i] = f[1][i - 1] + f[1][i - 1] + f[0][i - 1];
}
int sum = 0;
F(i, 0, K - 1) sum += f[0][i].len;
}
int find_p(int n)
{
SegInfo all = {0, 0, 0, 0};
F(i, 0, K - 1)
{
SegInfo nw = all + f[0][i];
if (n - nw.sum <= 2 * nw.len)
{
int t = 0;
FF(j, i - 1, 0)
{
if (t == 0)
{
nw = all + f[1][j];
if (n - nw.sum <= 2 * nw.len) t = 1;
else
{
t = 0;
all = nw;
}
}
else
{
nw = all + f[1][j];
if (n - nw.sum <= 2 * nw.len) t = 1;
else
{
all = nw;
nw = all + f[1][j];
if (n - nw.sum <= 2 * nw.len) t = 1;
else
{
t = 0;
all = nw;
}
}
}
}
return all.len + 1;
}
all = nw;
}
assert(0);
}
SegInfo getinfo(int m)
{
SegInfo all = {0, 0, 0, 0};
F(i, 0, K - 1)
{
if (all.len + f[0][i].len < m)
{
all = all + f[0][i];
continue;
}
int t = 0;
FF(j, i - 1, 0)
{
if (t == 0)
{
if (all.len + f[1][j].len >= m) t = 1;
else
{
t = 0;
all = all + f[1][j];
}
}
else
{
if (all.len + f[1][j].len >= m) t = 1;
else
{
all = all + f[1][j];
if (all.len + f[1][j].len >= m) t = 1;
else
{
t = 0;
all = all + f[1][j];
}
}
}
}
return all + f[t][0];
}
assert(0);
}
void work(void)
{
int n; cin >> n;
if (n == 1) return cout << "1\n", void();
if (n == 2) return cout << "1\n", void();
int p = find_p(n);
SegInfo xp = getinfo(p), xn = getinfo(n - 1);
if ((n - p) % 2 == 0)
{
if (p % 2 == 0) cout << p - xn.odd + xp.odd << '\n';
else cout << p - xn.even + xp.even << '\n';
}
else
{
if (p % 2 == 0) cout << n - xp.sum - p - xn.even + xp.even << '\n';
else cout << n - xp.sum - p - xn.odd + xp.odd << '\n';
}
}
signed main(void)
{
// freopen(".in", "r", stdin);
// freopen(".out", "w", stdout);
ios::sync_with_stdio(0), cin.tie(nullptr);
init();
int _; cin >> _;
while (_--) work();
return 0;
}