大佬帮忙看看萌新的分块哪里写的不对
查看原帖
大佬帮忙看看萌新的分块哪里写的不对
110009
Soul_Love楼主2023/8/23 15:15
#include<bits/stdc++.h>
#define ll long long
using namespace std;
int m,n,t,L[400],R[400],pos[100010],l,k,tag[400],a[100001],rt[400][100010],f[100010],mx[400],num[100010];
//L,R是块的两端,tag是块内减,rt[i][j]表示第i个块内j这个数的祖先,f是并查集的父亲数组,mx为块内最大值,num[i]表示在并查集中i的儿子数量(包括自己) 
inline int read()
{
    int k=0,f=0;char c=getchar();
    for(;!isdigit(c);c=getchar()) f|=c=='-';
    for(;isdigit(c);c=getchar()) k=(k<<1)+(k<<3)+(c^48);
    return f?-k:k;
}
inline int find(int k)
{
    if(f[k]==k) return k;
    return f[k]=find(f[k]);
}
inline void build(int k)//建块和重建块 
{
    mx[k]=0;
    for(int i=L[k];i<=R[k];i++)
    {
    	f[i]=i;
        num[i]=0;
        rt[k][a[find(i)]]=0;
    }
    for(int i=L[k];i<=R[k];i++)
    {
        if(!rt[k][a[i]]) rt[k][a[i]]=i;//如果这个数还没确立根节点,就直接令它为根节点 
        f[i]=rt[k][a[i]];
        num[rt[k][a[i]]]++;
        pos[i]=k;
        mx[k]=max(mx[k],a[i]);
    }
}
inline void prepare()
{
    int ql=sqrt(n);
    t=n/ql;
    for(int i=1;i<=t;i++)
    {
        L[i]=R[i-1]+1;
        R[i]=i*ql;
    }
    if(R[t]<n)
    {
        t++;
        L[t]=R[t-1]+1;
        R[t]=n;
    }
    for(int i=1;i<=t;i++) build(i);
}
inline void update(int l,int r,int k)
{
    int q=pos[l],p=pos[r];
    if(q==p)
    {
        for(int i=L[q];i<=R[q];i++) a[i]=a[find(i)];//在祖先处拿到真实的值,下同 
        for(int i=l;i<=r;i++)
		{
			if(a[i]-tag[q]>k)
			{
				rt[q][a[i]]=0;
				a[i]-=k;
			}
		}
        build(q);
        return;
    }
    for(int i=L[q];i<=R[q];i++) a[i]=a[find(i)];
    for(int i=L[p];i<=R[p];i++) a[i]=a[find(i)];
    for(int i=l;i<=R[q];i++)
	{
		if(a[i]-tag[q]>k)
		{
			rt[q][a[i]]=0;
			a[i]-=k;
		}
	}
    for(int i=L[p];i<=r;i++)
	{
		if(a[i]-tag[p]>k)
		{
			rt[p][a[i]]=0;
			a[i]-=k;
		}
	}
    build(q);
    build(p);
    for(int i=q+1;i<p;i++)
    {
        if(mx[i]-tag[i]<=(k<<1))//trick 
        {
            for(int j=k+1+tag[i];j<=mx[i];j++)
            {
                if(rt[i][j])
                {
                    if(!rt[i][j-k]) rt[i][j-k]=rt[i][j];//如果j-k这个数还没根节点,就令它为j-k的根节点 
                    f[rt[i][j]]=rt[i][j-k];//并上去 
                    if(rt[i][j-k]!=rt[i][j]) num[rt[i][j-k]]+=num[rt[i][j]];//累加数量 
                    rt[i][j]=0;
                    a[rt[i][j-k]]=j-k;//只修改祖先处的值 
//                    if(j==mx[i]) mx[i]-=k;//如果加上这句的话第4个点会Wa,可是我觉得更新mx是合理的啊 
                }
            }
        }
        else
        {
            for(int j=k+tag[i];j>=1+tag[i];j--)//同上 
            {
                if(rt[i][j])
                {
                    if(!rt[i][j+k]) rt[i][j+k]=rt[i][j];
                    f[rt[i][j]]=rt[i][j+k];
                    if(rt[i][j+k]!=rt[i][j]) num[rt[i][j+k]]+=num[rt[i][j]];
                    rt[i][j]=0;
                    a[rt[i][j+k]]=j+k;
                    mx[i]=max(mx[i],j+k);
                }
            }
            tag[i]+=k;
        }
    }
}
inline int ask(int l,int r,int k)
{
    int q=pos[l],p=pos[r],s=0;
    if(q==p)
    {
        for(int i=l;i<=r;i++) if(a[find(i)]-tag[q]==k) s++; 
        return s;
    }
    for(int i=l;i<=R[q];i++) if(a[find(i)]-tag[q]==k) s++;
    for(int i=L[p];i<=r;i++) if(a[find(i)]-tag[p]==k) s++;
    for(int i=q+1;i<p;i++) s+=num[rt[i][k+tag[i]]];
    return s;
}
int main()
{
    n=read(),m=read();
    for(int i=1;i<=n;i++)
    {
        a[i]=read();
        f[i]=i;
    }
    prepare();
    while(m--)
    {
        int o1=read(),o2=read(),o3=read(),o4=read();
        if(o1==1) update(o2,o3,o4);
        else printf("%d\n",ask(o2,o3,o4));
    }
    return 0;
}
2023/8/23 15:15
加载中...