AtCoder Regular Contest 150 F Constant Sum Subsequence

AtCoder 传送门

洛谷传送门

定义 nxt(i,x) 为最小的 j 满足 aj=xj>ipre(i,x) 为最大的 j 满足 aj=xj<i

有了上面的定义后,考虑 dp。设 fs 表示最小的 i,满足所有和为 s 的正整数序列都是 a1,a2,...,ai 的子序列。则答案为 fS(此处的 S 为原题面中的 S),初值 f0=0

考虑如何转移。枚举一个数 x,那么所有和为 sx 的序列都可以在末尾接上一个 x 得到,所以 fsnxt(fsx,x)。可得 fs=maxx=1snxt(fsx,x)。如果使用刷表法,则 fs 已知,fs+xmax(fs+x,nxt(fs,x))。这样朴素转移是 O(S2logn+n) 的。

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

总时间复杂度 O(n+SlogS(logS+logn))

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 @   zltzlt  阅读(24)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
点击右上角即可分享
微信分享提示