「LOJ2461」「2018 集训队互测 Day 1」完美的队列-分块

Description

n n std::queue<int>,编号为 1 1 n n

对于第 i i 个队列,当它的大小超过 a i a_i 时,它会执行pop()知道大小不超过 a i a_i

每次操作为把 [ l , r ] [l,r] 的队列push(x) x x 是给定的数。

每次执行完操作后,求所有队列中有多少种不同的数。

n , m , a i , x 1 0 5 n,m,a_i,x \leq 10^5

Solution

显然,当前插入的数再此队列被push a i a_i 次后会被弹出。

如果求出来每次插入的数被全部弹出的时间 e d i ed_i ,那么这道题就很好做了。

考虑把序列分块。

对于每次操作,它可以被拆分为若干个整块与 0 / 1 / 2 0/1/2 个散块。

首先考虑整块的贡献。显然,对于都包含了同一整块的操作,它们在这个整块的 e d ed 是单调不降的。所以可以用two-pointer维护。

维护两个指针 j , k j,k ,表示做完区间 ( j , k ] (j,k] 的操作后,操作 j j 加入的数会被全部弹出。设 c o v cov 表示当前块被完全覆盖了多少次, b i b_i 表示队列 i i 再被push b i b_i 次就会把 x j x_j 弹掉。 M a x Max b j b_j 的最大值。

考虑如何维护,当 k k 左移即加入一个操作时,如果它覆盖了整块,那么把 c o v cov 加一。否则如果它和当前区间有交,则把有交的部分的 b i b_i 减一,重新计算 M a x Max 。当 j j 左移即撤销一个操作时类似。

然后考虑散块的贡献。

在处理整块时,预处理如下的东西。

  • s i s_i 表示 1 i 1-i 次操作完整覆盖了当前块 s i s_i 次。
  • c i c_i 表示第 i i 个完整覆盖整块的操作。
  • d i d_i 表示第 i i 个未完整覆盖整块但有交的操作。
  • e i e_i 表示当有 i i 个未完整覆盖整块但有交的操作时有 e i e_i 个完整覆盖整块的操作。

枚举每个散块里的位置 i i ,考虑它对 d d 中操作的贡献。类似的,对于覆盖这个位置的操作,它们在这个位置的 e d ed 也是单调的。

d d 上维护双指针 j , k j,k 。表示当执行完 ( j , k ] (j,k] 的操作后,当前枚举位置的 x j x_j 已被弹出。维护一个 c n t cnt 表示当前位置被覆盖的次数。

对于每个 j j k k 不断往后跳直到 c n t 0 cnt\leq 0 。然后分情况讨论在何时当前位置的 x j x_j 被弹出。

#include <bits/stdc++.h>
using namespace std;

const int maxn = 100005;

int n, m, S;

struct operation
{
	int l, r, x;
} opt[maxn];

int cov, c_cnt, d_cnt, cnt, Max;
int a[maxn], b[maxn], s[maxn], c[maxn], d[maxn], e[maxn], ed[maxn];

map<int, int> f;
vector<int> vec[maxn];

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

inline void chkmax(int &a, int b) {if (a < b) a = b;}

int main()
{
	freopen("q1.in", "r", stdin);
	//freopen("queue.in", "r", stdin);
	freopen("queue.out", "w", stdout);

	n = gi(); m = gi();
	for (int i = 1; i <= n; ++i) a[i] = gi();
	for (int i = 1; i <= m; ++i) opt[i] = (operation) {gi(), gi(), gi()};

	S = sqrt(n);
	for (int L = 1, R; L <= n; L += S) {
		R = min(L + S - 1, n);

		c_cnt = d_cnt = cov = Max = 0;
		for (int i = L; i <= R; ++i) chkmax(Max, b[i] = a[i]);
		
		for (int i = 1, j = 0; i <= m; ++i) {
			if (opt[i].l <= L && R <= opt[i].r) --cov;
			else if (opt[i].l <= R && L <= opt[i].r) {
				for (int j = max(opt[i].l, L); j <= min(opt[i].r, R); ++j) ++b[j];
				Max = 0;
				for (int j = L; j <= R; ++j) chkmax(Max, b[j]);
			}

			while (j <= m && Max - cov > 0) {
				++j;
				if (opt[j].l <= L && R <= opt[j].r) ++cov;
				else if (opt[j].l <= R && L <= opt[j].r) {
					for (int k = max(opt[j].l, L); k <= min(opt[j].r, R); ++k) --b[k];
					Max = 0;
					for (int k = L; k <= R; ++k) chkmax(Max, b[k]);
				}	
			}

			s[i] = s[i - 1];
			if (opt[i].l <= L && R <= opt[i].r) chkmax(ed[i], j), ++s[c[++c_cnt] = i];
			else if (opt[i].l <= R && L <= opt[i].r) d[++d_cnt] = i, e[d_cnt] = c_cnt;
		}
		
		for (int i = L; i <= R; ++i) {
			cnt = a[i];
			for (int j = 1, k = 0; j <= d_cnt; ++j) {
				cnt += s[d[j]] - s[d[j - 1]];
				if (opt[d[j]].l <= i && i <= opt[d[j]].r) {
					++cnt;
					while (k < d_cnt && cnt > 0) ++k, cnt -= s[d[k]] - s[d[k - 1]] + (opt[d[k]].l <= i && i <= opt[d[k]].r);
					if (cnt > 0) {
						if (cnt > s[m] - s[d[k]]) ed[d[j]] = m + 1;
						else chkmax(ed[d[j]], c[e[k] + cnt]);
						continue;
					}
					if (opt[d[k]].l <= i && i <= opt[d[k]].r) chkmax(ed[d[j]], cnt ? c[e[k] + cnt + 1]: d[k]);
					else chkmax(ed[d[j]], c[e[k] + cnt]);
				}
			}
		}	
	}
	
	for (int i = 1; i <= m; ++i) vec[i].push_back(opt[i].x), vec[ed[i]].push_back(-opt[i].x);

	cnt = 0;
	for (int i = 1; i <= m; ++i) {
		for (int j = 0, siz = vec[i].size(); j < siz; ++j) 
			if (vec[i][j] > 0) {
				if (!f[vec[i][j]]) ++cnt;
				++f[vec[i][j]];
			} else {
				--f[-vec[i][j]];
				if (!f[-vec[i][j]]) --cnt;
			}
		printf("%d\n", cnt);
	}

	return 0;
}

猜你喜欢

转载自blog.csdn.net/DSL_HN_2002/article/details/85253921