【BZOJ3711】Druzyny

【BZOJ3711】Druzyny

题面

bzoj

题解

首先我们有一个\(O(n^2)\)\(dp\)

\(f_i\)表示现在已经分好了\(1...i\)的组,且\(i\)作为一组的结尾的最大值,那么转移的话就是对于每个
\(\max\limits_{k=j}^i c_k\leq i-j+1\leq \min\limits_{k=j}^i d_k\)\(f_{j-1}\)转移一下。

然后这个平方算法减下枝就艹过去了

然后想想怎么优化这个东西。

\(pre_i\)表示在仅考虑\(d\)的限制之下,对于某一右端点\(i\)可以转移过来的左端点\(j-1\)的最靠左的位置,那么上文的\(j\)对应的就是\([j+1,i]\)这个区间,而且\(pre\)肯定也是单调不降的。

我们考虑在这样子处理\(d\)限制的基础之上解决\(c\)限制,对于一个点的转移,肯定只与一段区间内\(c\)的最大值有关,考虑最大值分治。假设对于一段区间\([l,r]\)\([l+1,r]\)\(c\)所处的最大值位置为\(p\)(由上文所述,\(f_l\)是否能转移到\(f_r\)不取决于\(l\)的情况),然后递归处理\([l,p)\)再对于\(j\in [l,p)\),我们要求出\(f_j\)对于\(f_i,i\in [p,r]\)的贡献,最后递归\([p,r]\)

那么问题的关键在于对于\(\forall i\in [p,r]\)\(pre_i\)不降,在复杂度正确的情况下求出\(\forall j\in [l,p)\)对其的贡献。

下面分为四种情况解决这个问题:
\(\text{Case 1:}\)
\(pre_i\leq l,i-p+1< c_p\),此时这种\(i\)会由\([l...x](x< p)\)这段前缀转移过来,第一次用线段树找到最开始的前缀最大值然后随着\(i\)增加往后面推可以\(O(1)\)转移。
这种情况只会\(O(\log n)\)查一次以及一次\(\min(p-l,r-p+1)\)遍历一次,与最大值分治复杂度一致。
\(\text{Case 2:}\)
\(pre_i\leq l,i-p+1\geq c_p\),这时候整个左区间都能转移过来,二分一下\(pre_i\leq l\)最大的\(i\)然后线段树区间修改即可。
这种情况只会\(O(\log n)\)改一次以及\(O(\log n)\)二分一次。
\(\text{Case 3:}\)
\(pre_i>l\),直接暴力查线段树区间即可。
对于\(\forall i\),分治时只会出现至多一次这样的情况,总复杂度\(O(n\log n)\)
\(\text{Case 4:}\)
直接退出即可。

最后这题卡空间只能用线段树求区间最值,复杂度\(O(n\log n)\)在上面已经分析过了。

代码

这里是\(O(n^2)\)艹过去的

\(O(n\log n)\)

#include <iostream>
#include <cstdio>
#include <cstdlib>
#include <cstring> 
#include <cmath> 
#include <algorithm>
#include <queue> 
using namespace std; 
inline int gi() { 
    register int data = 0, w = 1; 
    register char ch = 0; 
    while (!isdigit(ch) && ch != '-') ch = getchar(); 
    if (ch == '-') w = -1, ch = getchar(); 
    while (isdigit(ch)) data = 10 * data + ch - '0', ch = getchar(); 
    return w * data; 
}
const int INF = 1e9; 
const int Mod = 1e9 + 7; 
const int MAX_N = 1e6 + 5; 
int N, c[MAX_N], d[MAX_N], pre[MAX_N]; 
struct Data { int f, g; } F[MAX_N]; 
Data operator + (const Data &l, const Data &r) { 
	if (l.f == r.f) return (Data){l.f, (l.g + r.g) % Mod}; 
	else { 
		if (l.f < r.f) return (Data){r.f, r.g}; 
		else return (Data){l.f, l.g}; 
	} 
} 
#define lson (o << 1) 
#define rson (o << 1 | 1) 
namespace SGT1 { 
	int maxp[MAX_N << 2]; 
	void build(int o, int l, int r) { 
		if (l == r) return (void)(maxp[o] = l); 
		int mid = (l + r) >> 1; 
		build(lson, l, mid), build(rson, mid + 1, r); 
		maxp[o] = c[maxp[lson]] > c[maxp[rson]] ? maxp[lson] : maxp[rson]; 
	} 
	int query(int o, int l, int r, int ql, int qr) { 
		if (ql <= l && r <= qr) return maxp[o]; 
		int mid = (l + r) >> 1, res = 0; 
		if (ql <= mid) res = query(lson, l, mid, ql, qr); 
		if (qr > mid) {
			int p = query(rson, mid + 1, r, ql, qr); 
			res = c[res] > c[p] ? res : p; 
		} 
		return res; 
	} 
} 
namespace SGT2 { 
	Data Max[MAX_N << 2], Tag[MAX_N << 2]; 
	void pushup(int o) { Max[o] = Max[lson] + Max[rson]; } 
	void puttag(int o, Data v) { Tag[o] = Tag[o] + v, Max[o] = Max[o] + v; } 
	void pushdown(int o) { 
		puttag(lson, Tag[o]); 
		puttag(rson, Tag[o]); 
		Tag[o] = (Data){-INF, 0}; 
	} 
	void build(int o, int l, int r) { 
		Max[o] = Tag[o] = (Data){-INF, 0}; 
		if (l == r) return ; 
		int mid = (l + r) >> 1; 
		build(lson, l, mid), build(rson, mid + 1, r); 
	} 
	void upd(int pos) { 
		int o = 1, l = 0, r = N; 
		while (l < r) { 
			pushdown(o); 
			int mid = (l + r) >> 1; 
			if (pos <= mid) o = lson, r = mid; 
			else o = rson, l = mid + 1; 
		} 
		F[pos] = Max[o] = F[pos] + Tag[o]; 
		while (o >>= 1) pushup(o); 
	}
	void modify(int o, int l, int r, int ql, int qr, Data v) { 
		if (ql <= l && r <= qr) return puttag(o, v);
		pushdown(o); 
		int mid = (l + r) >> 1; 
		if (ql <= mid) modify(lson, l, mid, ql, qr, v); 
		if (qr > mid) modify(rson, mid + 1, r, ql, qr, v); 
		pushup(o); 
	} 
	Data query(int o, int l, int r, int ql, int qr) {
		if (ql > qr) return (Data){-INF, 0}; 
		if (ql <= l && r <= qr) return Max[o]; 
		pushdown(o); 
		int mid = (l + r) >> 1; 
		Data res = (Data){-INF, 0}; 
		if (ql <= mid) res = res + query(lson, l, mid, ql, qr); 
		if (qr > mid) res = res + query(rson, mid + 1, r, ql, qr); 
		return res; 
	} 
} 
void init() { 
	SGT1::build(1, 1, N); 
	SGT2::build(1, 0, N); 
	static priority_queue<int, vector<int>, greater<int> > q1, q2; 
	for (int i = 1; i <= N; i++) { 
		pre[i] = pre[i - 1]; 
		q1.push(d[i]); 
		while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop(); 
		while (q1.top() < i - pre[i]) { 
			q2.push(d[++pre[i]]); 
			while (!q2.empty() && q1.top() == q2.top()) q1.pop(), q2.pop(); 
		} 
	} 
} 
void Div(int l, int r) { 
	if (l == r) return SGT2::upd(l); 
	int p = SGT1::query(1, 1, N, l + 1, r); 
	Div(l, p - 1); 
	int pos = max(p, l + c[p]); 
	Data nw = SGT2::query(1, 0, N, l, pos - c[p] - 1); 
	while (pos <= r && pre[pos] <= l && pos - c[p] < p) { 
		nw = nw + F[pos - c[p]]; 
		F[pos] = F[pos] + (Data){nw.f + 1, nw.g}; 
		++pos; 
	} 
	if (pos <= r && pre[pos] <= l) { 
		int L = pos, R = r; 
		while (L < R) {
			int M = (L + R + 1) >> 1; 
			if (pre[M] <= l) L = M; 
			else R = M - 1; 
		} 
		SGT2::modify(1, 0, N, pos, L, (Data){nw.f + 1, nw.g}); 
		pos = L + 1; 
	} 
	while (pos <= r && pre[pos] < p) { 
		nw = SGT2::query(1, 0, N, pre[pos], min(pos - c[p], p - 1)); 
		F[pos] = F[pos] + (Data){nw.f + 1, nw.g}; 
		++pos; 
	} 
	Div(p, r); 
} 
int main () { 
#ifndef ONLINE_JUDGE 
    freopen("cpp.in", "r", stdin); 
#endif 
	N = gi(); 
	for (int i = 1; i <= N; i++) c[i] = gi(), d[i] = gi(); 
	for (int i = 1; i <= N; i++) F[i] = (Data){-INF, 0}; 
	F[0] = (Data){0, 1}; 
	init(); 
	Div(0, N); 
	if (F[N].f <= 0) puts("NIE");
	else printf("%d %d\n", F[N].f, F[N].g); 
    return 0; 
} 
posted @ 2019-10-24 11:43  heyujun  阅读(363)  评论(2编辑  收藏  举报