首页 > 其他分享 >【题解】P6578 [Ynoi2019] 魔法少女网站

【题解】P6578 [Ynoi2019] 魔法少女网站

时间:2023-01-15 21:00:47浏览次数:52  
标签:Ynoi2019 const 分块 int 题解 sz maxn 考虑 P6578

卡了一晚上终于过了。

好家伙,又是想题想一半不会是吧,小垃圾是不是想退役 /fad

小黑子 -> 小垃圾 -> 垃圾酱 -> 垃圾摇滚 /xk

但是真的有垃圾摇滚这东西 /kk

思路

操作分块 + 普通分块。

题意将区间内的数分成两类:小于等于 \(x\) 的和大于 \(x\) 的。可以考虑一个套路:将第一类数设为 \(1\),将第二类数设为 \(0\).

那么设 \([l, r]\) 区间内极长 \(1\) 连续段的长度分别是 \(l_1, ..., l_m\),则显然答案是 \(\sum\limits_{i = 1}^m \frac{l_m (l_m + 1)}{2}\)

于是只需要考虑维护区间内极长的连续 \(1\) 段即可。

在考虑这个东西之前还有一个问题。\(x\) 不是单调的,所以要同时维护增删。增加可以直接考虑分讨,但是删除比较难做,所以要考虑令 \(x\) 一定程度上单调,这样就只需要维护一种操作。

一种朴素的想法是把所有询问离线下来按 \(x\) 排序,但是这样修改就需要考虑偏序问题。感觉上比较麻烦,但也可能有不用操作分块的做法。

所以我们希望在一定范围内令 \(x\) 单调的同时保证复杂度可过,所以可以想到类似根号分治的东西,也就是操作分块。

简单考虑,操作分块的本质实际上是在 \(O(n)\) 左右的时间完成 \(O(\sqrt{n})\) 次修改和 \(O(\sqrt{n})\) 次查询,也可以考虑成对时间分治。

一般需要考虑的贡献关系是:

  1. 前面的整块 -> 当前的整块

  2. 当前的整块 -> 当前的整块

那么只需要对操作序列进行适当地分块,然后在块内把询问排一次序,这样就可以保证询问的 \(x\) 单调。

在 \(x\) 单调的情况下,只需要考虑把 \(0\) 变成 \(1\) 就行。

维护极长连续 \(1\) 段的话,一个 \(0\) 变成 \(1\) 的贡献可以分讨出来:

  1. 成为某个极长 \(1\) 段的端点

  2. 连通位于其左右两侧的极长 \(1\) 段

考虑对于极长 \(1\) 段的端点维护其另一个端点的位置,这样合并就可以 \(O(1)\) 类似链表做。这个直接上分块就行。

因为块内修改对块内贡献需要考虑操作的先后顺序,所以不写奇怪东西的话每次查询都需要重新加入修改的贡献。

关于上面的回溯问题,因为修改的时候只会修改几个点的信息,直接记录一下原本的信息,结束之后再覆盖回去就行。这样的复杂度是块长,没有问题。

对于前面整块的贡献,因为查询的 \(x\) 单调,所以处理单块时可以考虑直接将当前序列中小于等于 \(\min x\) 的数加入贡献,结束后再根据这个块中的修改操作更改序列。

设操作分块块长为 \(B_1\),普通分块块长为 \(B_2\),则 \(B_1, B_2\) 和 \(\sqrt{n}\) 同阶时,时间复杂度大约为:

\(O(\frac{m}{B_1} (B_1 \log B_1 + B_2) + \frac{n}{B_2} B_2)\)

需要大力卡一下常:

  1. 科技快读快写

  2. inline + register + const + &

  3. 信仰 cp 学号块长 \(B_1 = 1821, B_2 = 516\)

  4. 预处理一些常数大且常用的东西

  5. 卡评测机波动

代码

#include <cstdio>
#include <cmath>
#include <cstring>
// #include <vector>
#include <algorithm>
// using namespace std;

namespace IO
{
    //by cyffff
	int len = 0;
	char ibuf[(1 << 20) + 1], *iS, *iT, out[(1 << 26) + 1];
	#define gh() (iS == iT ? iT = (iS = ibuf) + fread(ibuf, 1, (1 << 20) + 1, stdin), (iS == iT ? EOF : *iS++) : *iS++)
	#define reg register

	inline int read()
    {
		reg char ch = gh();
		reg int x = 0;
		reg char t = 0;
		while (ch < '0' || ch > '9') t |= ch == '-', ch = gh();
		while (ch >= '0' && ch <= '9') x = x * 10 + (ch ^ 48), ch = gh();
		return t ? -x : x;
	}

	inline void putc(char ch) { out[len++] = ch; }

	template<class T>

	inline void write(T x)
    {
		if (x < 0) putc('-'), x = -x;
		if (x > 9) write(x / 10);
		out[len++] = x % 10 + 48;
	}

	inline void flush()
    {
		fwrite(out, 1, len, stdout);
		len = 0;
	}
}
using IO::read;
using IO::write;
using IO::flush;
using IO::putc;

#define reg
typedef long long ll;

const int maxn = 3e5 + 1;
const int maxm = 3e5 + 1;
const int sz = 2e3 + 1;

struct Modify
{
    int t, pos, val;
} q1[sz];

struct Query
{
    int t, l, r, x, idx;
} q2[sz];

struct node
{
    int idx, res, t[4];
    bool flag;

    inline void clear() { t[0] = t[1] = t[2] = t[3] = flag = 0; }
} stk[sz], tmp;

struct Edge
{
    int to, nxt;
} edge[maxn];

int n, m, sqn, sqm, tot, c1, c2, top, ecnt;
int st[sz], ed[sz];
int head[maxn], a[maxn], bel[maxn], pos[maxn];
bool vis[maxn], chg[maxn], used[maxn], seq[maxn];
ll gt[maxn];
ll ans[sz], sum[sz];
// vector<int> idx[maxn];

inline int min(reg const int &a, reg const int &b) { return (a <= b ? a : b); }
inline bool cmp(const Query& x, const Query& y) { return (x.x < y.x); }

inline void modify(reg const int& p, reg bool flag)
{
    pos[p] = p, seq[p] = true, tmp.idx = p;
    reg const int lst = p - 1, nxt = p + 1, tmp_p = pos[lst];
    reg const bool flag1 = (seq[lst] && (st[bel[p]] != p)), flag2 = (seq[nxt] && (ed[bel[p]] != p));
    if ((!flag1) && (!flag2)) tmp.clear(), tmp.res = 1;
    else
    {
        tmp.flag = true;
        if (flag1 && flag2)
        {
            tmp.res = (nxt - pos[lst]) * (pos[nxt] - lst);
            tmp.t[0] = pos[lst], tmp.t[1] = pos[pos[lst]], pos[pos[lst]] = pos[nxt];
            tmp.t[2] = pos[nxt], tmp.t[3] = pos[pos[nxt]], pos[pos[nxt]] = tmp_p;
        }
        else if(flag1)
        {
            tmp.res = nxt - pos[lst];
            tmp.t[0] = p, tmp.t[1] = pos[p], pos[p] = pos[lst];
            tmp.t[2] = pos[lst], tmp.t[3] = pos[pos[lst]], pos[pos[lst]] = p;
        }
        else
        {
            tmp.res = pos[nxt] - lst;
            tmp.t[0] = p, tmp.t[1] = pos[p], pos[p] = pos[nxt];
            tmp.t[2] = pos[nxt], tmp.t[3] = pos[pos[nxt]], pos[pos[nxt]] = p;
        }
    }
    sum[bel[p]] += tmp.res;
    if (flag) stk[++top] = tmp;
}

inline ll query(const int& l, const int& r)
{
    reg ll ans = 0;
    if (bel[l] == bel[r])
    {
        reg int cnt = 0;
        for (reg int i = l; i <= r; i++)
            if (seq[i]) cnt++;
            else ans += gt[cnt], cnt = 0;
        return ans + gt[cnt];
    }
    reg int cnt1 = 0, cnt2 = 0;
    for (reg int i = l; i <= ed[bel[l]]; i++)
        if (seq[i]) cnt1++;
        else ans += gt[cnt1], cnt1 = 0;
    for (reg int i = r; i >= st[bel[r]]; i--)
        if (seq[i]) cnt2++;
        else ans += gt[cnt2], cnt2 = 0;
    reg int res = cnt1;
    for (reg int i = bel[l] + 1; i <= bel[r] - 1; i++)
    {
        if (pos[st[i]] == ed[i]) res += ed[i] - st[i] + 1;
        else
        {
            if (seq[st[i]]) res += pos[st[i]] - st[i] + 1, ans -= gt[pos[st[i]] - st[i] + 1];
            ans += gt[res] + sum[i], res = 0;
            if (seq[ed[i]]) res += ed[i] - pos[ed[i]] + 1, ans -= gt[ed[i] - pos[ed[i]] + 1];
        }
    }
    return ans + gt[res + cnt2];
}

inline void add_edge(const int& u, const int& v)
{
    edge[++ecnt] = (Edge){v, head[u]};
    head[u] = ecnt;
}

inline void solve()
{
    memset(seq, false, sizeof(seq));
    memset(pos, 0, sizeof(pos));
    memset(sum, 0, sizeof(sum));
    for (reg int i = 1; i <= c1; i++) chg[q1[i].pos] = true;
    for (reg int i = 1; i <= n; i++)
        if (!chg[i]) add_edge(a[i], i);
    std::sort(q2 + 1, q2 + c2 + 1, cmp);
    reg int lim = 1;
    for (reg int i = 1; i <= c2; i++)
    {
        while (lim <= q2[i].x)
        {
            for (reg int& i = head[lim]; i; i = edge[i].nxt) modify(edge[i].to, 0);
            lim++;
        }
        for (reg int j = c1; j >= 1; j--)
            if ((q1[j].t < q2[i].t) && (!used[q1[j].pos]))
            {
                used[q1[j].pos] = true;
                if (q1[j].val <= q2[i].x) modify(q1[j].pos, 1);
            }
        for (reg int j = 1; j <= c1; j++)
            if (!used[q1[j].pos])
            {
                used[q1[j].pos] = true;
                if (a[q1[j].pos] <= q2[i].x) modify(q1[j].pos, 1);
            }
        ans[q2[i].idx] = query(q2[i].l, q2[i].r);
        while (top)
        {
            tmp = stk[top--], sum[bel[tmp.idx]] -= tmp.res, seq[tmp.idx] = false;
            if (tmp.flag) pos[tmp.t[2]] = tmp.t[3], pos[tmp.t[0]] = tmp.t[1];
        }
        for (int j = 1; j <= c1; j++) used[q1[j].pos] = false;
    }
    ecnt = 0;
    memset(head, 0, sizeof(head));
    // for (int i = 0; i <= n; i++) idx[i].clear();
    for (int i = 1; i <= c1; i++) chg[q1[i].pos] = false;
}

int main()
{
    n = read(), m = read(), sqn = 516, sqm = 1821;
    tot = (n + sqn - 1) / sqn;
    for (reg int i = 1; i <= tot; i++) st[i] = ed[i - 1] + 1, ed[i] = (i == tot ? n : i * sqn);
    for (reg int i = 1; i <= n; i++) a[i] = read(), bel[i] = (i - 1) / sqn + 1, gt[i] = 1ll * i * (i + 1) / 2;
    for (reg int i = 1, j; i <= m; i = j + 1)
    {
        j = min(m, i + sqm), c1 = c2 = 0;
        for (reg int k = i; k <= j; k++)
            if (read() == 1) q1[++c1] = (Modify){k, read(), read()};
            else q2[++c2] = (Query){k, read(), read(), read(), c2};
        solve();
        for (reg int k = 1; k <= c2; k++) write(ans[k]), putc('\n');
        for (reg int k = 1; k <= c1; k++) a[q1[k].pos] = q1[k].val;
    }
    flush();
    return 0;
}

标签:Ynoi2019,const,分块,int,题解,sz,maxn,考虑,P6578
From: https://www.cnblogs.com/lingspace/p/p6578.html

相关文章

  • Atcoder Regular Contest ARC 153 A B C D 题解
    点我看题A-AABCDDEFE一个beautifulnumber是形如这样的:\(S1S1S3S4S5S5S7S8S7\)。如果选定了\(S1\),后面的数有100000种选法,所以先求出答案的\(S1\)。假设现在我们要求出......
  • Atcoder Regular Contest ARC 153 A B C D 题解
    点我看题A-AABCDDEFE一个beautifulnumber是形如这样的:\(S1S1S3S4S5S5S7S8S7\)。如果选定了\(S1\),后面的数有100000种选法,所以先求出答案的\(S1\)。假设现在我们要求出......
  • Atcoder Regular Contest ARC 153 A B C D 题解
    点我看题A-AABCDDEFE一个beautifulnumber是形如这样的:\(S1S1S3S4S5S5S7S8S7\)。如果选定了\(S1\),后面的数有100000种选法,所以先求出答案的\(S1\)。假设现在我们要求出......
  • 【题解】P5397 [Ynoi2018] 天降之物
    码力人的甜品,口嗨者的末路。感觉手牵手那个题才是第四分块正体,这个不如叫最初根号分治。思路根号分治。对于每个值,把它们分成出现大于根号次和小于等于根号次两类。先......
  • 【题解】P5692 [MtOI2019]手牵手走向明天
    春节前做大分块是什么奇妙传统吗……这个题好想是好想,但是写起来就是另外一回事了……思路分块。第四分块加强版。区间查询,根号分治做法寄了。看到合并颜色可以想到......
  • Codeforces 732 Div2 A-D题解
    感觉做过这场啊,要不就是看过A题AquaMoonandTwoArrays问前加后减能不能把A变成B,首先这个貌似是经典老题了,无论怎么操作数列总和不变,如果和不相同,变不了,其他情况暴力判......
  • ARC153 ABC 题解
    A【题意】给定\(N\),求第\(N\)个满足以下条件的数:它是一个\(9\)位数,没有前导\(0\)。它的第一位等于它的第二位。它的第五位等于它的第六位。它的第七位等于它......
  • XMU 2023.1.14 题解汇总
    A、CF1779A原题B、https://www.cnblogs.com/wondering-world/p/17038860.htmlC、https://www.luogu.com.cn/problem/solution/P4305D、快速幂模板点击查看代码#incl......
  • DTOJ-2023-01-02-测试-题解
    (2023省选模拟Round#4)之前感冒了一阵子,错过了两场省选模拟,不过我不打算补(乐成绩:0+42+0(就是说T1写挂了)A题目链接题目大意小\(\omega\)最近学习了分治\(\text{......
  • 【题解】P4565 [CTSC2018]暴力写挂
    能写点分为什么要写这种玄学东西。思路边分树合并。首先考虑点分,发现只会T飞的做法。但是答案的形式有点意思,换一下写法:\(ans=\frac{1}{2}\max(\operatorname{dis......