AtCoder Regular Contest 150 F Constant Sum Subsequence

AtCoder 传送门

洛谷传送门

定义 \(\mathrm{nxt}(i,x)\) 为最小的 \(j\) 满足 \(a_j = x\)\(j > i\)\(\mathrm{pre}(i,x)\) 为最大的 \(j\) 满足 \(a_j = x\)\(j < i\)

有了上面的定义后,考虑 dp。设 \(f_s\) 表示最小的 \(i\),满足所有和为 \(s\) 的正整数序列都是 \(a_1,a_2,...,a_i\) 的子序列。则答案为 \(f_S\)(此处的 \(S\) 为原题面中的 \(S\)),初值 \(f_0 = 0\)

考虑如何转移。枚举一个数 \(x\),那么所有和为 \(s-x\) 的序列都可以在末尾接上一个 \(x\) 得到,所以 \(f_s \ge \mathrm{nxt}(f_{s-x},x)\)。可得 \(f_s = \max\limits_{x=1}^s \mathrm{nxt}(f_{s-x},x)\)。如果使用刷表法,则 \(f_s\) 已知,\(f_{s+x} \gets \max(f_{s+x},\mathrm{nxt}(f_s,x))\)。这样朴素转移是 \(O(S^2 \log n + n)\) 的。

考虑分治优化。设当前区间为 \([l,r]\),令 \(mid = \left\lfloor\frac{l+r}{2}\right\rfloor\)。先计算 \([l,mid]\),再考虑 \([l,mid]\)\([mid+1,r]\) 的贡献。还是先枚举一个 \(x\),由 \(f\) 的转移式可得 \(f\) 有单调性,则 \(\forall i \in [mid+1,r],f_i > f_{mid}\)。则我们只需要考虑可能对右区间有贡献的 \(s\),即 \(s\) 满足 \(\mathrm{nxt}(f_s,x) = \mathrm{nxt}(f_{mid},x)\)。这些 \(s\) 形成了一段区间,而这个区间的左端点就是最小的 \(i\) 满足 \(f_i \ge \mathrm{pre}(f_{mid},x)\)。于是对这段区间内的 \(s\),令 \(f_{s+x} \gets \max(f_{s+x},\mathrm{nxt}(f_{mid},x))\)。则我们需要一个区间取 \(\max\),单点查询的数据结构。可以用标记永久化的线段树实现。

总时间复杂度 \(O(n + S \log S(\log S + \log n))\)

code
/*

p_b_p_b txdy
AThousandSuns txdy
Wu_Ren txdy
Appleblue17 txdy

*/

#include <bits/stdc++.h>
#define pb push_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))

using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef long double ldb;
typedef pair<ll, ll> pii;

#define getchar() (p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 1 << 21, stdin), p1 == p2) ? EOF : *p1++)
char buf[1 << 21], *p1 = buf, *p2 = buf;
inline ll read() {
    char c = getchar();
    ll x = 0;
    for (; !isdigit(c); c = getchar()) ;
    for (; isdigit(c); c = getchar()) x = (x << 1) + (x << 3) + (c ^ 48);
    return x;
}

const int maxn = 1500100;
const int maxm = 200100;

ll n, m, a[maxn], f[maxm];
vector<ll> app[maxm];

namespace SGT {
	ll tree[maxm << 2];
	
	void update(int rt, int l, int r, int ql, int qr, ll x) {
		if (ql <= l && r <= qr) {
			tree[rt] = max(tree[rt], x);
			return;
		}
		int mid = (l + r) >> 1;
		if (ql <= mid) {
			update(rt << 1, l, mid, ql, qr, x);
		}
		if (qr > mid) {
			update(rt << 1 | 1, mid + 1, r, ql, qr, x);
		}
	}
	
	ll query(int rt, int l, int r, int x) {
		if (l == r) {
			return tree[rt];
		}
		int mid = (l + r) >> 1;
		if (x <= mid) {
			return max(tree[rt], query(rt << 1, l, mid, x));
		} else {
			return max(tree[rt], query(rt << 1 | 1, mid + 1, r, x));
		}
	}
}

inline ll getnxt(ll x, ll y) {
	ll t = (x - 1) % n + 1, k = x - t;
	auto it = upper_bound(app[y].begin(), app[y].end(), t);
	if (it == app[y].end()) {
		return app[y].front() + k + n;
	} else {
		return *it + k;
	}
}

inline ll getpre(ll x, ll y) {
	ll t = (x - 1) % n + 1, k = x - t;
	auto it = upper_bound(app[y].begin(), app[y].end(), t);
	if (it == app[y].begin()) {
		return app[y].back() + k - n;
	} else {
		return *(--it) + k;
	}
}

void solve(int l, int r) {
	if (l == r) {
		return;
	}
	int mid = (l + r) >> 1;
	solve(l, mid);
	for (int i = 1; i <= r - l; ++i) {
		int R = min(r - i, mid);
		int L = lower_bound(f + l, f + mid + 1, getpre(f[R], i)) - f;
		L = max(L, mid + 1 - i);
		SGT::update(1, 0, m, L + i, R + i, getnxt(f[R], i));
	}
	for (int i = mid + 1; i <= r; ++i) {
		f[i] = SGT::query(1, 0, m, i);
	}
	solve(mid + 1, r);
}

void solve() {
	n = read();
	m = read();
	for (int i = 1; i <= n; ++i) {
		a[i] = read();
		app[a[i]].pb(i);
	}
	solve(0, m);
	printf("%lld\n", f[m]);
}

int main() {
	int T = 1;
	// scanf("%d", &T);
	while (T--) {
		solve();
	}
	return 0;
}

posted @ 2022-12-14 18:48  zltzlt  阅读(23)  评论(0编辑  收藏  举报