P4117 Ynoi2018 五彩斑斓的世界

P4117 Ynoi2018 五彩斑斓的世界

听说 gal 圈神作,有空推。

思路

最近在做值域分块,形式和这玩意很像。但发现,没有好的数据结构维护相同的值位置,且值域没有想象中的大。

考虑正常的序列分块。对于 \(mi>x\) 的块打上整体减的 \(tag\),否则暴力处理该块的情况,用并查集维护块内相同的数的位置和个数。

利用上值域的性质,发现一个块减去的和小于 \(V(\leq 10^5)\)。我们枚举出需要修改的并查集,并且让我们的枚举量的总和为 \(V\) 就能以 \(O(V\sqrt n)\) 的时间通过本题。

设块内最大值为 \(mx\)

\(mx\leq 2\cdot x\),遍历大于 \(x\) 的并查集并进行修改,时间复杂度 \(O(mx-x)\),最大值至少下降 \(mx-x\)

\(mx>2\cdot x\),遍历小于 \(x\) 的并查集并进行修改,时间复杂度 \(O(x)\),最大值至少下降 \(x\)

最大值下降程度与时间复杂度同级,故单块时间复杂度为 \(O(n)\)

同时你仔细思考后会发现,如果不按照上面的偏序关系遍历,总是会花费更多的时间遍历,但最大值下降程度却比复杂度低。

到这里题解就要结束了……吗?

看到空间限制 64mb……

我们正常的分块是每个块开一个并查集,花销为 \(O(V\sqrt n+n)\)\(V\) 是每一个块内,记录每个值对应的并查集根部的数组。

注意到,现在的做法是可以强制在线的……

实际上,本体中每一个块是极为独立的,任意一个块的操作都无法对其他块产生影响。

启发我们把每一个块拆下来,这 \(O(\sqrt n)\) 个块都依次做 \(m\) 个操作,这样就可以共用一个权值数组,数组大小缩小至 \(O(V+n)\)

到这里题解就要结束了……吗?

你会发现有点卡常,在快读、inline 等基础卡常下给出一下建议。

  1. 少一切无必要或可合并的循环,比如统计散块答案不用遍历整个区间。
  2. 使用 C++98 O2 提交。
  3. 调整合适的块长,我是 \(\sqrt n\) 块长过的。

CODE

// #pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int maxn=1e6+5,maxm=5e5+5,maxV=1e5+5,smaxn=1e3+5;

int n,m,B;
int a[maxn],ans[maxm];

struct worknode{int op,l,r,x;}chg[maxm];

int l,r,tag,mx;

inline int read()
{
    int sum=0,f=1;
    char c=getchar();
    while(c!='-'&&(c<'0'||c>'9')) c=getchar();
    if(c=='-') f=-1,c=getchar();
    while('0'<=c&&c<='9') sum=(sum<<1)+(sum<<3)+c-'0',c=getchar();
    return sum*f;
}
inline void write(int x) {
    static int sta[35];
    int top = 0;
    do {
    sta[top++] = x % 10;
    x /= 10;
    } while (x);
    while (top) putchar(sta[--top] + '0');
}

//DSU
int rt[maxV],fa[maxn],sz[maxn],frt[maxn];
inline int fr(int u){return fa[u]==u?u:fa[u]=fr(fa[u]);}
inline void merge(int u,int v)
{
    if(!rt[v]) rt[v]=rt[u],frt[rt[v]]=v;
    else
    {
        if(sz[rt[u]]<sz[rt[v]])
            fa[rt[u]]=rt[v],sz[rt[v]]+=sz[rt[u]],frt[rt[u]]=0;
        else
        {
            fa[rt[v]]=rt[u],sz[rt[u]]+=sz[rt[v]];
            frt[rt[v]]=0,frt[rt[u]]=v;
            rt[v]=rt[u];
        }
    }
    rt[u]=0;
}

inline void rebuild(bool flg)
{
    mx=tag=0;
    if(flg) memset(rt,0,sizeof(rt));
    else for(int i=l;i<=r;i++) rt[frt[i]]=0;
    for(int i=l;i<=r;i++)
    {
        fa[i]=sz[i]=frt[i]=0;
        mx=max(a[i],mx);
        if(rt[a[i]]) fa[i]=rt[a[i]],sz[fa[i]]++;
        else rt[a[i]]=fa[i]=i,sz[i]=1,frt[i]=a[i];
    }
}
inline void modify(int val)
{
    if(mx-tag<=val) return ;
    if(mx-tag>=(val<<1))
    {
        for(int i=1+tag;i<=val+tag;i++) if(rt[i]) merge(i,i+val);
        tag+=val;
    }
    else
    {
        for(int i=mx;i>val+tag;i--) if(rt[i]) merge(i,i-val);
        if(tag+val<mx) mx=tag+val;
    }
}
inline void pushtag(){for(int i=l;i<=r;i++) a[i]=frt[fr(i)]-tag;}
inline int qry(int val)
{
    if(val+tag>maxV-1) return 0;
    return sz[rt[val+tag]];
}

int main()
{
    // freopen("data.in","r",stdin);
    // freopen("data.out","w",stdout);
    n=read(),m=read();B=sqrt(n);
    for(int i=1;i<=n;i++) a[i]=read();
    for(int i=1;i<=m;i++)
    {
        int op,l,r,x;
        op=read(),l=read(),r=read(),x=read();
        chg[i]={op,l,r,x};
    }
    for(int i=1;i<=(n-1)/B+1;i++)
    {
        // cerr<<i<<" "<<clock()<<"\n";
        l=r+1,r=min(l+B-1,n);
        rebuild(1);
        for(int k=1;k<=m;k++)
        {
            if(chg[k].r<l||chg[k].l>r) continue;
            if(chg[k].op==1)
            {
                if(chg[k].l<=l&&r<=chg[k].r) modify(chg[k].x);
                else
                {
                    pushtag();
                    for(int i=max(chg[k].l,l);i<=min(chg[k].r,r);i++) a[i]-=(a[i]>chg[k].x?chg[k].x:0);
                    rebuild(0);
                }
            }
            else
            {
                if(chg[k].l<=l&&r<=chg[k].r) ans[k]+=qry(chg[k].x);
                else
                    for(int i=max(chg[k].l,l);i<=min(chg[k].r,r);i++) ans[k]+=((frt[fr(i)]-tag)==chg[k].x);
            }
        }
    }
    for(int i=1;i<=m;i++) if(chg[i].op==2) write(ans[i]),putchar('\n');
}
posted @ 2025-02-23 22:01  彬彬冰激凌  阅读(24)  评论(0)    收藏  举报