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;
}

待更

posted @ 2020-09-28 09:15  Grice  阅读(337)  评论(0编辑  收藏  举报