P6578 魔法少女网站

P6578 魔法少女网站

给定一个长为 nn 的序列,定义区间 [l,r][l,r] 的子区间为所有形如 [l,r][l',r'] 的区间,满足 l,rl',r' 为整数,且 llrrl \le l' \le r' \le r

mm 次操作:

  • 1 x y:将 xx 位置的值修改为 yy
  • 2 l r x:查询区间 [l,r][l,r] 中有多少子区间的最大值小于或等于 xx

1n,m3×1051 \leq n,m\leq 3\times 10^5,时限 3.50s3.50\text{s},空限 64MB64\text{MB}

sol

第十分块。

难度评分:66

对于查询,考虑贡献,容易发现,设 b[i]=(a[i]x)b[i]=(a[i] \leq x),那么我们现在的问题就是询问区间内 bb 的所有为 11 的极长子区间的 siz×(siz+1)2\frac{siz \times(siz+1)}{2} 之和。

再考虑怎么维护这个极长子区间。

由于 xx 随时会变,相当于就是我们每次会修改一些点 010 \to 1 或者 101 \to 0

对于 010 \to 1 这种情况,我们考虑把询问按 xx 升序排序,然后我们发现这个满足的数会越来越多,然后分类讨论几种情况:

  • 单独一个区间,直接加。
  • 在一个区间的左边,直接加。
  • 在一个区间的右边,直接加。
  • 连接两个区间,修改区间信息后直接加。

但是对于 101 \to 0 这种情况,即修改时可能会出现的情况,每一次都需要考虑,复杂度显然错误。

类似于回滚莫队的思想,将一个东西变成没有删除的。

容易发现 101 \to 0 这种情况只会在这 mm 个操作中出现,先将有修改的位置空出来,然后每一次询问时再将这些位置插入进去,记录一下改变的答案和指针个数,询问完了撤回即可,这样的话这些操作全部变成了将 00 换成 0011,就可以维护了。

但是我们发现,这些有修改的位置最多有 mm 个,每次询问都要修改,那么总时间复杂度是 O(m2)\mathcal O(m^2) 的,直接爆炸。

考虑操作分块:每 m\sqrt{m} 次重建原序列,然后暂时储存的 m\sqrt{m} 次操作一起做。

这样这些有修改的位置最多有 m\sqrt{m} 个,每次询问都要修改,那么单个操作块的时间复杂度是 O(m×m)=O(m)\mathcal O(\sqrt{m} \times \sqrt m)=\mathcal O(m) 的,总时间复杂度是 O(mn\mathcal O(m \sqrt{n} 的,可过。

查询时,散块记录,整块逐块扫过去即可。

所以总时间复杂度为 O(nn)\mathcal O(n \sqrt n),总空间复杂度为 O(n)\mathcal O(n)

总结:序列分块加操作分块再加大力卡常。

01-29 17:25:11 / 1.23min / 22.48MB / 5.82KB C++98 O2\text{01-29 17:25:11 / 1.23min / 22.48MB / 5.82KB C++98 O2}

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>

#define re register

namespace Fread
{
    const int SIZE = 1 << 23;
    char buf[SIZE], *S, *T;
    inline char getchar()
    {
        if (S == T)
        {
            T = (S = buf) + fread(buf, 1, SIZE, stdin);
            if (S == T)
                return '\n';
        }
        return *S++;
    }
}
namespace Fwrite
{
    const int SIZE = 1 << 23;
    char buf[SIZE], *S = buf, *T = buf + SIZE;
    inline void flush()
    {
        fwrite(buf, 1, S - buf, stdout);
        S = buf;
    }
    inline void putchar(char c)
    {
        *S++ = c;
        if (S == T)
            flush();
    }
    struct NTR
    {
        ~NTR()
        {
            flush();
        }
    } ztr;
}

#ifdef ONLINE_JUDGE
#define getchar Fread::getchar
#define putchar Fwrite::putchar
#endif

inline int read()
{
	re int x = 0, f = 1;
	re char c = getchar();
	while(c < '0' || c > '9')
	{
		if(c == '-') f = -1;
		c = getchar();
	}
	while(c >= '0' && c <= '9')
	{
		x = (x << 3) + (x << 1) + (c ^ 48);
		c = getchar();
	}
	return x * f;
}

inline void write(re long long x)
{
	if(x > 9)
		write(x / 10);
	putchar(x % 10 + 48);
}

inline int min(re const int &x, re const int &y)
{
	return x < y ? x : y;
}

const int N = 3e5 + 1, S = 2e3 + 1;

struct Modify
{
	int opt, pos, val;
} q1[S];

struct Query
{
	int opt, l, r, x, id;
} q2[S];

int n, m, c1, c2, a[N];

long long calc[N];

bool seq[N];

int L[S], R[S], id[N], p[N], sum[S];

inline void clear()
{
	memset(seq, 0, sizeof seq);
	memset(p, 0, sizeof p);
	memset(sum, 0, sizeof sum);
}

inline void init()
{
	re int sqn = sqrt(n * 4 / 5 + 1);
	for(re int i = 1, j, c = 1; i <= n; i = j + 1, ++c)
	{
		L[c] = i, R[c] = j = min(n, i + sqn);
		for(re int t = L[c]; t <= R[c]; ++t)
			id[t] = c;
	}
}

int top;

struct Node
{
	int id, ans, t1[4];
	bool flg;
	inline void clear()
	{
		t1[0] = t1[1] = t1[2] = t1[3] = flg = 0;
	}
} sta[S], tmp;

inline void modify(re const int &pos, re bool flg)
{
	p[pos] = pos, seq[pos] = 1, tmp.id = pos;
	re const int lst = pos - 1, nxt = pos + 1, tmp_p = p[lst];
	re const bool flg1 = (seq[lst] && L[id[pos]] != pos), flg2 = (seq[nxt] && R[id[pos]] != pos);
	if(!flg1 && !flg2)
		tmp.clear(), tmp.ans = 1;
	else
	{
		tmp.flg = 1;
		if(flg1 && flg2)
		{
			tmp.ans = (nxt - p[lst]) * (p[nxt] - lst);
			tmp.t1[0] = p[lst], tmp.t1[1] = p[p[lst]], p[p[lst]] = p[nxt];
			tmp.t1[2] = p[nxt], tmp.t1[3] = p[p[nxt]], p[p[nxt]] = tmp_p;
		}
		else
		{
			if(flg1)
			{
				tmp.ans = nxt - p[lst];
				tmp.t1[0] = pos, tmp.t1[1] = p[pos], p[pos] = p[lst];
				tmp.t1[2] = p[lst], tmp.t1[3] = p[p[lst]], p[p[lst]] = pos;
			}
			else
			{
				tmp.ans = p[nxt] - lst;
				tmp.t1[0] = pos, tmp.t1[1] = p[pos], p[pos] = p[nxt];
				tmp.t1[2] = p[nxt], tmp.t1[3] = p[p[nxt]], p[p[nxt]] = pos;
			}
		}
	}
	sum[id[pos]] += tmp.ans;
	if(flg) sta[++top] = tmp;
}

inline void back()
{
	while(top)
	{
		tmp = sta[top--], sum[id[tmp.id]] -= tmp.ans, seq[tmp.id] = 0;
		if(tmp.flg)
		{
			p[tmp.t1[2]] = tmp.t1[3];
			p[tmp.t1[0]] = tmp.t1[1];
		}
	}
}

inline long long query(const int &l, const int &r)
{
	re long long ans = 0;
	if(id[l] == id[r])
	{
		int cnt = 0;
		for(re int i = l; i <= r; ++i)
			if(seq[i])
				cnt++;
			else
				ans += calc[cnt], cnt = 0;
		return ans + calc[cnt];
	}
	re int c1 = 0, c2 = 0;
	for(re int i = l; i <= R[id[l]]; ++i)
		if(seq[i])
			c1++;
		else
			ans += calc[c1], c1 = 0;
	for(re int i = r; i >= L[id[r]]; --i)
		if(seq[i])
			c2++;
		else
			ans += calc[c2], c2 = 0;
	re int tot = c1;
	for(re int i = id[l] + 1; i <= id[r] - 1; ++i)
		if(p[L[i]] == R[i])
			tot += R[i] - L[i] + 1;
		else
		{
			if(seq[L[i]])
			{
				tot += p[L[i]] - L[i] + 1, ans -= calc[p[L[i]] - L[i] + 1];
			}
			ans += calc[tot] + sum[i], tot = 0;
			if(seq[R[i]])
			{
				tot += R[i] - p[R[i]] + 1, ans -= calc[R[i] - p[R[i]] + 1];
			}
		}
	return ans + calc[tot + c2];
}

long long ans[S];

int cnt, head[N];

struct Edge
{
	int to, nxt;
} G[N];

bool broke[N], use[N];

inline void add(const int &u, const int &v)
{
	G[++cnt] = {v, head[u]};
	head[u] = cnt;
}

inline bool cmp(const Query &x, const Query &y)
{
	return x.x < y.x;
}

inline void solve()
{
	clear();
	for(re int i = 1; i <= c1; ++i)
		broke[q1[i].pos] = 1;
	for(re int i = 1; i <= n; ++i)
		if(!broke[i])
			add(a[i], i);
	std::sort(q2 + 1, q2 + c2 + 1, cmp);
	int pot = 1;
	for(re int i = 1; i <= c2; ++i)
	{
		while(pot <= q2[i].x)
		{
			for(re int i = head[pot]; i; i = G[i].nxt)
				modify(G[i].to, 0);
			pot++;
		}
		for(re int j = c1; j >= 1; --j)
			if(q1[j].opt < q2[i].opt && !use[q1[j].pos])
			{
				use[q1[j].pos] = 1;
				if(q1[j].val <= q2[i].x)
					modify(q1[j].pos, 1);
			}
		for(re int j = 1; j <= c1; ++j)
			if(!use[q1[j].pos])
			{
				use[q1[j].pos] = 1;
				if(a[q1[j].pos] <= q2[i].x)
					modify(q1[j].pos, 1);
			}
		ans[q2[i].id] = query(q2[i].l, q2[i].r);
		back();
		for(re int j = 1; j <= c1; ++j)
			use[q1[j].pos] = 0;
	}
	cnt = 0;
	memset(head, 0, sizeof head);
	for(re int i = 1; i <= c1; ++i)
		broke[q1[i].pos] = 0;
}

signed main()
{
	n = read(), m = read();
	int sqm = sqrt(m * 11);
	init();
	for(re int i = 1; i <= n; ++i)
		a[i] = read();
	for(re int i = 1; i <= n; ++i)
		calc[i] = 1ll * i * (i + 1) / 2;
	for(re int i = 1, j; i <= m; i = j + 1)
	{
		j = min(m, i + sqm), c1 = c2 = 0;
		for(re int t = i; t <= j; ++t)
			if(read() == 1)
				q1[++c1] = {t, read(), read()};
			else
				q2[++c2] = {t, read(), read(), read(), c2};
		solve();
		for(re int t = 1; t <= c2;++t)
			write(ans[t]), putchar('\n');
		for(re int t = 1; t <= c1; ++t)
			a[q1[t].pos] = q1[t].val;
	}
}
posted @ 2022-01-29 17:46  蒟蒻orz  阅读(23)  评论(0编辑  收藏  举报  来源