Description
从前一个和谐的班级所有人都是搞 的。有 个是男生,有 个是女生。
现在所有男生排成一行,老师想把他们分成若干个小组写动态仙人掌,每个小组包含连续的一段人,每个人属于且仅属于一个小组。
第
个人所在的小组人数应该在区间
内。
老师想知道这个班级里最多产生多少个小组,以及有多少种方案能达到最大值。由于方案数可能很大,你只需要输出方案数对
取模的结果。
Solution
首先有一个很好写的 DP。
设 表示最多划分数, 表示方案数, 表示 合法的决策集合。
可以发现如果只考虑 的限制,那么 的左端点 是单调上升的。而加上 的限制后,区间内的某些部分就被删去成为了零散的几部分。
似乎不好优化,此时通过分治转化限制条件是一种很好的方法。
考虑最值分治,设区间 中,最大的 在 处, 处理 到 的转移。可以发现,所有的转移的 都等于 ,且对于 ,它的转移区间不断右移。
对于 ,它的转移区间为 。把 分为两部分,第一部分满足 ,第二部分满足 。
对于第一部分,从 开始,先在线段树上查询它的转移区间,然后每次右移 时,在之前结果上增加 。当转移区间右端点 时,意味着剩下的点的右端点也满足 。所以二分出第一部分点的结束位置 ,在线段树上给 打区间覆盖标记即可。
这样做其实是 的,因为 的移动不会超过右区间,而转移区间右端点的移动不会超过左区间。
对于第二部分,每次在线段树上查询转移区间的最值转移即可。(因为对于每一个位置,只会转移被 “切开”的满足 条件的某个部分,总时间复杂度是 )
tips.注意一下这题卡空间
#include <bits/stdc++.h>
using namespace std;
inline int gi()
{
char c = getchar();
while (c < '0' || c > '9')
c = getchar();
int sum = 0;
while ('0' <= c && c <= '9')
sum = sum * 10 + c - 48, c = getchar();
return sum;
}
const int maxn = 1000001, Inf = 1 << 30, mod = 1000000007;
int n, c[maxn], d[maxn];
#define mid ((l + r) >> 1)
#define lch (s << 1)
#define rch (s << 1 | 1)
pair<int, int> mxc[maxn * 3];
void build_c(int s, int l, int r)
{
if (l == r) return mxc[s] = make_pair(c[l], l), void();
build_c(lch, l, mid);
build_c(rch, mid + 1, r);
mxc[s] = max(mxc[lch], mxc[rch]);
}
pair<int, int> query_c(int s, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr) return mxc[s];
if (qr <= mid) return query_c(lch, l, mid, ql, qr);
else if (ql > mid) return query_c(rch, mid + 1, r, ql, qr);
else return max(query_c(lch, l, mid, ql, qr), query_c(rch, mid + 1, r, ql, qr));
}
struct node
{
int x, y;
node() {x = y = 0;}
node(int _x, int _y) {x = _x; y = _y;}
node operator + (const node &a) const {
if (x == a.x) return node(x, y + a.y > mod ? y + a.y - mod : y + a.y);
if (x < a.x && a.y) return a;
return node(x, y);
}
node inc() {return node(x + 1, y);}
} mxf[maxn * 3], lzy[maxn * 3], f[maxn];
void modify1(int s, int l, int r, int ql, int qr, node v)
{
if (ql <= l && r <= qr) return lzy[s] = lzy[s] + v, void();
if (ql <= mid) modify1(lch, l, mid, ql, qr, v);
if (qr > mid) modify1(rch, mid + 1, r, ql, qr, v);
}
void modify2(int s, int l, int r, int p)
{
f[p] = f[p] + lzy[s];
if (l == r) return mxf[s] = f[p], void();
if (p <= mid) modify2(lch, l, mid, p);
else modify2(rch, mid + 1, r, p);
mxf[s] = mxf[lch] + mxf[rch];
}
node query_f(int s, int l, int r, int ql, int qr)
{
if (ql <= l && r <= qr) return mxf[s];
if (qr <= mid) return query_f(lch, l, mid, ql, qr);
else if (ql > mid) return query_f(rch, mid + 1, r, ql, qr);
return query_f(lch, l, mid, ql, qr) + query_f(rch, mid + 1, r, ql, qr);
}
int Left[maxn];
inline int calc(int l, int r, int lim)
{
int Mid;
while (l < r) {
Mid = (l + r + 1) >> 1;
if (Left[Mid] <= lim) l = Mid;
else r = Mid - 1;
}
return l;
}
void trans(int l, int k, int r)
{
int p = max(l + c[k], k), L = max(l, Left[p]), R = min(p - c[k], k - 1);
if (p > r || L >= k) return ;
node tmp = L > R ? node(0, 0) : query_f(1, 0, n, L, R);
while (p <= r && L <= l) {
if (R >= k - 1) {
int q = calc(p, r, l);
modify1(1, 0, n, p, q, tmp.inc());
p = q + 1;
break;
}
f[p] = f[p] + tmp.inc();
tmp = tmp + f[++R];
L = max(L, Left[++p]);
}
while (p <= r) {
L = Left[p]; R = min(p - c[k], k - 1);
if (L > k) break;
f[p] = f[p] + (L > R ? node(0, 0) : query_f(1, 0, n, L, R).inc());
++p;
}
}
void solve(int l, int r)
{
if (l > r) return ;
if (l == r) return modify2(1, 0, n, l);
int k = query_c(1, 0, n, l + 1, r).second;
solve(l, k - 1);
trans(l, k, r);
solve(k, r);
}
int main()
{
freopen("druzyny.in", "r", stdin);
freopen("druzyny.out", "w", stdout);
n = gi();
for (int i = 1; i <= n; ++i) c[i] = gi(), d[i] = gi();
build_c(1, 0, n);
static int q[maxn], s = 1, t = 0;
q[++t] = 1; Left[1] = 1;
for (int i = 2; i <= n; ++i) {
Left[i] = Left[i - 1];
while (s <= t && d[q[t]] > d[i]) --t;
q[++t] = i;
while (i - Left[i] + 1 > d[q[s]]) {
if (q[s] == Left[i]) ++s;
++Left[i];
}
}
for (int i = 1; i <= n; ++i) --Left[i];
f[0] = node(0, 1);
modify2(1, 0, n, 0);
solve(0, n);
if (f[n].y) printf("%d %d\n", f[n].x, f[n].y);
else puts("NIE");
return 0;
}