「BZOJ3711」「PA2014」Druzyny-分治

Description

从前一个和谐的班级所有人都是搞 O I OI 的。有 n n 个是男生,有 0 0 个是女生。

现在所有男生排成一行,老师想把他们分成若干个小组写动态仙人掌,每个小组包含连续的一段人,每个人属于且仅属于一个小组。
i i 个人所在的小组人数应该在区间 [ c i , d i ] [c_i,d_i] 内。
老师想知道这个班级里最多产生多少个小组,以及有多少种方案能达到最大值。由于方案数可能很大,你只需要输出方案数对 1000000007 1000000007 取模的结果。

n 1000000 n \leq 1000000

Solution

首先有一个很好写的 n 2 n^2 DP。

f i f_i 表示最多划分数, g i g_i 表示方案数, s i s_i 表示 i i 合法的决策集合。

f i = max j s i { f j } + 1 f_i=\max_{j \in s_i} \{ f_j\}+1
g i = j s i f j + 1 = f i g j g_i=\sum_{j \in s_i f_j+1=f_i}g_j

可以发现如果只考虑 d d 的限制,那么 s i s_i 的左端点 L e f t i Left_i 是单调上升的。而加上 c c 的限制后,区间内的某些部分就被删去成为了零散的几部分。

似乎不好优化,此时通过分治转化限制条件是一种很好的方法。

考虑最值分治,设区间 ( l , r ] (l,r] 中,最大的 c c k k 处, s o l v e ( l , r ) solve(l,r) 处理 [ l , k ) [l,k) [ k , r ] [k,r] 的转移。可以发现,所有的转移的 m a x c max_c 都等于 c k c_k ,且对于 i = k . . r i=k..r ,它的转移区间不断右移。

对于 i [ k , r ] i \in [k,r] ,它的转移区间为 [ m a x ( l , L e f t i ) , m i n ( i c k , k 1 ) ] [max(l, Left_i),min(i-c_k,k-1)] 。把 i i 分为两部分,第一部分满足 L e f t i l Left_i\leq l ,第二部分满足 L e f t i > l Left_i > l

对于第一部分,从 m a x ( l + c [ k ] , k ) max(l+c[k], k) 开始,先在线段树上查询它的转移区间,然后每次右移 p p 时,在之前结果上增加 f p c k f_{p-c_k} 。当转移区间右端点 k 1 \geq k-1 时,意味着剩下的点的右端点也满足 k 1 \geq k-1 。所以二分出第一部分点的结束位置 q q ,在线段树上给 [ p , q ] [p,q] 打区间覆盖标记即可。

这样做其实是 O ( m i n ( k l , r k + 1 ) ) O(min(k-l,r-k+1)) 的,因为 p p 的移动不会超过右区间,而转移区间右端点的移动不会超过左区间。

对于第二部分,每次在线段树上查询转移区间的最值转移即可。(因为对于每一个位置,只会转移被 L e f t i Left_i “切开”的满足 c c 条件的某个部分,总时间复杂度是 O ( n l o g n ) O(nlogn)

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;
}

猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/89401390
今日推荐