洛谷 P5979 [PA2014] Druzyny
简要题意
有
个人,把他们划分成尽可能多的区间,其中第 个人要求它所在的区间长度大于等于 ,小于等于 ,求最多的区间数量以及如此划分的方案数。 数据范围:
。时间限制7s(优秀解500-700ms,我的垃圾大常数实现2s,我都觉得写假了)。
题解
方案数是 trivial 的,实现一个 Data
类,合并信息的时候同时维护最大值和方案数即可。注意信息失去了幂等性。
首先考虑朴素 DP,设
下面考虑优化。注意到线性结构,转移彼此独立,考虑分治优化。题解区许多解法似乎是标准 CDQ 分治做法,然而时间复杂度是双
这题处理的难点在于我要同时维护
然而最值分治失去了分治层数
-
或者 :很容易排除掉。 -
:区间是 的前缀,随着 不断增加,右端点只会每次移动 1,那么第一次在线段树上查一下,后面就直接用 来 更新即可。这一部分的时间复杂度:第一次查线段树 ,后面的更新次数只会是左右区间长度的 ,类比启发式合并可以得到时间复杂度是 。 -
:区间是 ,二分一下右边能被这样更新的 ,一定也是个区间,线段树上区间查一下,区间取 一下, 。 -
:全部暴力 ,这是因为对于一个 ,它所属的所有分治右区间,所对应的分治左区间两两不交(可以画出分治树看一下),所以 只属于其中某一个,于是每个 只会这样暴力转移一次。
于是这样做完了,总复杂度
代码
// Author: kyEEcccccc #include <bits/stdc++.h> using namespace std; using LL = long long; using ULL = unsigned long long; #define F(i, l, r) for (int i = (l); i <= (r); ++i) #define FF(i, r, l) for (int i = (r); i >= (l); --i) #define MAX(a, b) ((a) = max(a, b)) #define MIN(a, b) ((a) = min(a, b)) #define SZ(a) ((int)((a).size()) - 1) const int N = 1000005, MOD = 1000000007; int n, c[N], d[N]; struct Data { int mx; LL s; }; Data mer(Data x, Data y) { Data r = {max(x.mx, y.mx), 0}; if (x.mx == r.mx) r.s += x.s; if (y.mx == r.mx) (r.s += y.s) %= MOD; return r; } struct SegTree { Data x[N << 2], tg[N << 2]; void init(int p, int cl, int cr) { x[p] = tg[p] = {-n, 0}; if (cl == cr) return; int cm = cl + cr >> 1; init(p << 1, cl, cm); init(p << 1 | 1, cm + 1, cr); } void pushdown(int p) { Data d = tg[p]; tg[p] = {-n, 0}; x[p << 1] = mer(x[p << 1], d); x[p << 1 | 1] = mer(x[p << 1 | 1], d); tg[p << 1] = mer(tg[p << 1], d); tg[p << 1 | 1] = mer(tg[p << 1 | 1], d); } Data get_max(int p, int cl, int cr, int l, int r) { if (l > r) return {-n, 0}; if (cl >= l && cr <= r) return x[p]; pushdown(p); int cm = cl + cr >> 1; if (l <= cm && r > cm) { return mer(get_max(p << 1, cl, cm, l, r), get_max(p << 1 | 1, cm + 1, cr, l, r)); } else if (l <= cm) return get_max(p << 1, cl, cm, l, r); else return get_max(p << 1 | 1, cm + 1, cr, l, r); } void assign_max(int p, int cl, int cr, int l, int r, Data d) { if (l > r) return; if (cl >= l && cr <= r) { x[p] = mer(x[p], d), tg[p] = mer(tg[p], d); return; } pushdown(p); int cm = cl + cr >> 1; if (l <= cm) assign_max(p << 1, cl, cm, l, r, d); if (r > cm) assign_max(p << 1 | 1, cm + 1, cr, l, r, d); x[p] = mer(x[p << 1], x[p << 1 | 1]); } } sgt; int pre[N]; Data f[N]; struct SegTree2 { int x[N << 2]; void init(int p, int cl, int cr) { if (cl == cr) return x[p] = cl, void(); int cm = cl + cr >> 1; init(p << 1, cl, cm); init(p << 1 | 1, cm + 1, cr); if (c[x[p << 1]] > c[x[p << 1 | 1]]) x[p] = x[p << 1]; else x[p] = x[p << 1 | 1]; } int get_max(int p, int cl, int cr, int l, int r) { if (cl >= l && cr <= r) return x[p]; int cm = cl + cr >> 1; if (l <= cm && r > cm) { int a = get_max(p << 1, cl, cm, l, r), b = get_max(p << 1 | 1, cm + 1, cr, l, r); return c[a] > c[b] ? a : b; } else if (l <= cm) return get_max(p << 1, cl, cm, l, r); else return get_max(p << 1 | 1, cm + 1, cr, l, r); } } sgt2; void solve(int l, int r) { if (l == r) { Data t = f[l]; f[l] = mer(f[l], sgt.get_max(1, 0, n, l, l)); sgt.assign_max(1, 0, n, l, l, t); return; } int mid = sgt2.get_max(1, 1, n, l + 1, r) - 1, mxc = c[mid + 1]; solve(l, mid); int p = max(mid + 1, l + mxc); Data cur_x = {INT_MIN, 0}; while (p <= r && p - mxc < mid && pre[p] <= l) { if (cur_x.mx == INT_MIN) cur_x = sgt.get_max(1, 0, n, l, p - mxc); else cur_x = mer(cur_x, f[p - mxc]); Data x = cur_x; ++x.mx; f[p] = mer(f[p], x); ++p; } if (p > r) goto SOLVE_R; if (pre[p] <= l) { int cl = p, cr = r, ca = p; while (cl <= cr) { int cm = cl + cr >> 1; if (pre[cm] <= l) ca = cm, cl = cm + 1; else cr = cm - 1; } Data x = sgt.get_max(1, 0, n, l, mid); ++x.mx; sgt.assign_max(1, 0, n, p, ca, x); p = ca + 1; } if (p > r) goto SOLVE_R; while (p <= r && pre[p] <= mid) { Data x = sgt.get_max(1, 0, n, pre[p], min(mid, p - mxc)); ++x.mx; f[p] = mer(f[p], x); ++p; } SOLVE_R: solve(mid + 1, r); } struct SegTree3 { int x[N << 2]; void init(int p, int cl, int cr) { if (cl == cr) return x[p] = d[cl], void(); int cm = cl + cr >> 1; init(p << 1, cl, cm); init(p << 1 | 1, cm + 1, cr); x[p] = min(x[p << 1], x[p << 1 | 1]); } int get_min(int p, int cl, int cr, int l, int r) { if (cl >= l && cr <= r) return x[p]; int cm = cl + cr >> 1; if (l <= cm && r > cm) { return min(get_min(p << 1, cl, cm, l, r), get_min(p << 1 | 1, cm + 1, cr, l, r)); } else if (l <= cm) return get_min(p << 1, cl, cm, l, r); else return get_min(p << 1 | 1, cm + 1, cr, l, r); } } sgt3; signed main(void) { // freopen(".in", "r", stdin); // freopen(".out", "w", stdout); ios::sync_with_stdio(0), cin.tie(nullptr); cin >> n; F(i, 1, n) cin >> c[i] >> d[i]; sgt3.init(1, 1, n); pre[1] = 0; F(i, 2, n) { pre[i] = pre[i - 1]; while (sgt3.get_min(1, 1, n, pre[i] + 1, i) < i - pre[i]) ++pre[i]; // cout << i << ": " << pre[i] << '\n'; } f[0] = {0, 1}; F(i, 1, n) f[i] = {-n, 0}; sgt.init(1, 0, n); sgt2.init(1, 1, n); solve(0, n); // F(i, 0, n) cout << i << ": " << f[i].mx << ' ' << f[i].s << '\n'; if (f[n].s == 0) cout << "NIE" << endl; else cout << f[n].mx << ' ' << f[n].s << endl; return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现