Codeforces Round #673 (Div. 1)
A
将每个值单独拿出来判断
B
由于\(a_i\ge 1\),可以将所有的\(a_i(i\ge 2)\)移到\(1\)
比赛时没看到这个东西,乱搞了八发才过
大概就是每次贪心把能移走的最大值移走,不知道正确性?
C
从小到大处理前\(i\)位,相同的一起处理,比赛时代码每次重新去重了\(O(nlognlogV)\),交了几发才过去
放到trie处理可以省掉去重的\(logn\)
D
启发式分裂可做到\(O(nlog^2n+qlogn)\)
官方题解给出了一个log的做法
克鲁斯卡重构树,这样可以把连通块转换到序列做
E
来更E了 嘻嘻嘻
看清题意,比赛的时候看成去重了,实际仅对于相邻相同的数字去重
有个很显然的朴素dp
令\(f_{i,j}\)为前\(i\)个数字分解\(b_{2i}=j\),转移显然
令\(curDP\)为\(f_i\),\(nxtDP\)为\(f_{i+1}\)
观察1:\(nxtDP_i\ge mx(curDP)\)
观察2:\(max(nxtDP_i)-min(nxtDP_i)\le 2\)
令\(X=a_{i+1}\)(以下数组下标均假设在有意义的情况)
考虑增量的贡献,有\(curDP_{X-i}+1\longrightarrow nxtDP_i\),特殊的,若\(X\)为偶数,\(curDP_{X/2}+2\longrightarrow nxtDP_{X/2}\)
于是,在增量为\(+2\)时,我们分如下两种情况
- \(max-min\le 1\):\(curDP_{X-i}+1\longrightarrow nxtDP_i\)。
- \(max-min=2\):用\(max(curDP_i)\)的最大值更新\(nxtDP_i\)。
容易想到记录\(S_0=\{i|curDP_i=min\},S_1=\{i|curDP_i=min+1\},S_2=\{i|curDP_i=min+2\}\)
注意到\(|S_2|le 1\)。可以用单个变量表示\(S_2\),仅记录\(S_1\)即可描述\(curDP\)
我们考虑进数组下标的意义,会发现\(curDP_{X-i}+1\longrightarrow nxtDP_i\)实际是关于\(X\)进行翻转
可以维护翻转的中心
考虑\(X\)对\(A\)对称:\(A-X\)
考虑\(A-X\)对\(B\)对称:\(B-A+X\)
\(C-B+A-X\)、\(D-C+B-A+X\)
可以维护\(X\)的系数及常数,对于系数为\(1\)或\(-1\)分别处理
下面是std
// chrono::system_clock::now().time_since_epoch().count()
#include<bits/stdc++.h>
#define __DBG 1
#define pb push_back
#define eb emplace_back
#define mp make_pair
#define fi first
#define se second
#define all(x) (x).begin(), (x).end()
#define debug(x) if (__DBG) cerr << #x << " = " << x << endl;
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
const int MAXN = (int)5e5 + 5;
int arr[MAXN];
int n;
void solve() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) {
scanf("%d", &arr[i]);
}
int zero = 0, two = -1;
set<pll> one;
ll shifter = 0;
int sign = 1;
for (int i = 1; i <= n; ++i) {
int x = arr[i];
if (two != -1) {
zero += 2;
sign = 1;
shifter = 0;
one.clear();
if (two < x) {
one.insert(mp(x - two, x - two));
}
}
else if (!one.empty()) {
zero++;
if (sign == -1) {
// shifter - idx >= x
// shifter - x >= idx
// idx <= shifter - x
ll lim = shifter - x;
while (!one.empty() && one.begin() -> se <= lim) {
one.erase(one.begin());
}
if (!one.empty() && one.begin() -> fi <= lim) {
pll it = mp(lim + 1, one.begin() -> se);
one.erase(one.begin());
assert(it.fi <= it.se);
one.insert(it);
}
} else {
// shifter + idx >= x
// idx >= x - shifter
ll lim = x - shifter;
while (!one.empty() && one.rbegin() -> fi >= lim) {
one.erase(--one.end());
}
if (!one.empty() && one.rbegin() -> se >= lim) {
pll it = mp(one.rbegin() -> fi, lim - 1);
one.erase(--one.end());
assert(it.fi <= it.se);
one.insert(it);
}
}
shifter = x - shifter;
sign *= -1;
}
else {//Set为空
sign = -1;
shifter = x;
int lim = min(arr[i - 1] - 1, x - 1);
if (1 <= lim) {
one.insert(mp(1, lim));
}
}
// consider x/2!
two = -1;
if (x % 2 == 0) {
int y = x / 2;
ll val = (y - shifter) / sign;
auto it = one.lower_bound(mp(val + 1, (ll)-1e18));
bool found = 0;
if (it != one.begin()) {
--it;
if (it -> fi <= val && val <= it -> se) {
found = 1;
}
}
if (found) {
two = y;
} else {
one.insert(mp(val, val));
}
}
}
int ans;
if (two != -1) {
ans = zero + 2;
} else if (!one.empty()) {
ans = zero + 1;
} else {
ans = zero;
}
printf("%d\n", 2 * n - ans);
}
int main() {
int tt;
scanf("%d", &tt);
while (tt--) {
solve();
}
return 0;
}
待更
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步