题目大意
给定一个n个数的序列。m次操作:1. 给区间[l,r]中所有大于x的数减x 2. 询问区间[l,r]中数值为x的数个数。
n,m≤100000 1≤a[i],x≤100000
分析
这题?循环展开然后optimize一下就过了
这是由乃题,我们应该考虑分块。
值域范围比较小,我们可以直接记录cnt[i][j]表示块i中数值为j的数个数。
然后再用并查集把同一块中数值相同的元素缩在一起。
对于第一个操作:首先两边的散块可以暴力修改。
对于中间的块:设mx表示该块中的最大值。分以下几种情况:
1. x≥mx,此时不用做任何修改
2. x < mx < 2*x,此时可以暴力把区间[x+1,mx]和[1,mx-x]对应的元素合并
3. 2*x≤mx,我们注意到:第一个操作等价于先把所有元素减x,然后把小于等于0的加x。所以我们可以设一个tag[i]表示块i中这种情况减少的总数。对于这种情况,先暴力把区间[1,x]和[x+1,x+x]对应的元素合并,然后tag[i]+=x
第二个操作:两边的散块暴力查询,中间的ans+=cnt[i][x+tag[i]]即可。
我们发现,对于修改操作:每一块合并的次数不超过100000次。
所以整体时间复杂度是
O(mn√)
#include <cstdio>
#include <cstring>
#include <algorithm>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const int N=1e5+5,G=320;
typedef long long LL;
int n,m,f[N],cnt[N],Id[N],val[N],b[N],tag[G],a[314][N],mx[G],ans;
char c;
int read()
{
int x=0,sig=1;
for (c=getchar();c<'0' || c>'9';c=getchar()) if (c=='-') sig=-1;
for (;c>='0' && c<='9';c=getchar()) x=x*10+c-48;
return x*sig;
}
int Get(int x)
{
return (f[x]==x)?x:f[x]=Get(f[x]);
}
void Merge(int x,int y)
{
f[y]=x; cnt[x]+=cnt[y];
}
void Change1(int l,int r,int x)
{
int i,j;
for (j=l;Id[j-1]==Id[j];j--);
for (i=j;Id[i]==Id[r];i++)
{
b[i]=val[Get(i)]; a[Id[i]][b[i]]=0;
}
mx[Id[r]]=0;
for (i=l;i<=r;i++) if (b[i]-tag[Id[i]]>x) b[i]-=x;
for (i=j;Id[i]==Id[r];i++)
{
f[i]=i; cnt[i]=1; val[i]=b[i];
mx[Id[i]]=max(mx[Id[i]],val[i]);
if (!a[Id[i]][val[i]]) a[Id[i]][val[i]]=i;else Merge(a[Id[i]][val[i]],i);
}
}
void Change2(int l,int r,int x)
{
for (int i,j=l;j<=r;j++) if (tag[j]+x<mx[j])
{
if (tag[j]+(x<<1)<=mx[j])
{
for (i=tag[j]+x;i>tag[j];i--) if (a[j][i])
{
if (!a[j][i+x]) val[a[j][i+x]=a[j][i]]=i+x;else Merge(a[j][i+x],a[j][i]);
a[j][i]=0;
}
tag[j]+=x;
}else
{
for (i=tag[j]+x+1;i<=mx[j];i++) if (a[j][i])
{
if (!a[j][i-x]) val[a[j][i-x]=a[j][i]]=i-x;else Merge(a[j][i-x],a[j][i]);
a[j][i]=0;
}
for (i=tag[j]+x;!a[j][i];i--);
mx[j]=i;
}
}
}
int main()
{
n=read(); m=read();
for (int i=1;i<=n;i++)
{
Id[i]=i/G; f[i]=i; cnt[i]=1;
val[i]=read(); mx[Id[i]]=max(mx[Id[i]],val[i]);
if (!a[Id[i]][val[i]]) a[Id[i]][val[i]]=i;else Merge(a[Id[i]][val[i]],i);
}
Id[0]=Id[n+1]=-1;
for (int typ,l,r,x,i;m--;)
{
typ=read(); l=read(); r=read(); x=read();
if (typ==1)
{
if (Id[l]==Id[r])
{
Change1(l,r,x);
continue;
}
for (i=l;Id[i+1]==Id[l];i++);
Change1(l,i,x);
for (i=r;Id[i-1]==Id[r];i--);
Change1(i,r,x);
Change2(Id[l]+1,Id[r]-1,x);
}else
{
ans=0;
if (Id[l]==Id[r])
{
for (i=l;i<=r;i++) ans+=(val[Get(i)]-tag[Id[i]]==x);
}else
{
for (i=l;Id[i]==Id[l];i++) ans+=(val[Get(i)]-tag[Id[i]]==x);
for (i=r;Id[i]==Id[r];i--) ans+=(val[Get(i)]-tag[Id[i]]==x);
for (i=Id[l]+1;i<Id[r];i++) if (x+tag[i]<N) ans+=cnt[a[i][x+tag[i]]];
}
printf("%d\n",ans);
}
}
return 0;
}