AtCoder Regular Contest 150 F Constant Sum Subsequence
定义 为最小的 满足 且 , 为最大的 满足 且 。
有了上面的定义后,考虑 dp。设 表示最小的 ,满足所有和为 的正整数序列都是 的子序列。则答案为 (此处的 为原题面中的 ),初值 。
考虑如何转移。枚举一个数 ,那么所有和为 的序列都可以在末尾接上一个 得到,所以 。可得 。如果使用刷表法,则 已知,。这样朴素转移是 的。
考虑分治优化。设当前区间为 ,令 。先计算 ,再考虑 对 的贡献。还是先枚举一个 ,由 的转移式可得 有单调性,则 。则我们只需要考虑可能对右区间有贡献的 ,即 满足 。这些 形成了一段区间,而这个区间的左端点就是最小的 满足 。于是对这段区间内的 ,令 。则我们需要一个区间取 ,单点查询的数据结构。可以用标记永久化的线段树实现。
总时间复杂度 。
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; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通