数列分块入门
1. 数列分块入门1
区间修改,单点查询
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e4+5;
int n,len,cnt;
int a[MAXN],tag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
if(x>y)return;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
a[i]+=k;
return;
}
for(register int i=x;i<=r[pos[x]];i++)a[i]+=k;
for(register int i=l[pos[y]];i<=y;i++)a[i]+=k;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=n/len+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/len+1;
}
for(register int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)add(x,y,c);
if(op==1)printf("%lld\n",a[y]+tag[pos[y]]);
}
return 0;
}
2. 数列分块入门2
区间修改,询问区间中小于 \(c\) 的数的个数
用 \(b\) 数组记录每个块排序之后的数组,以便二分查询排名
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],b[MAXN],tag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
if(x>y)return;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
a[i]+=k;
return;
}
for(register int i=x;i<=r[pos[x]];i++)a[i]+=k;
for(register int i=l[pos[y]];i<=y;i++)a[i]+=k;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
inline int ask(int x,int y,int k)
{
int sum=0;
if(x>y)return 0;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
if(a[i]+tag[pos[i]]<k)sum++;
return sum;
}
for(register int i=x;i<=r[pos[x]];i++)if(a[i]+tag[pos[i]]<k)sum++;
for(register int i=l[pos[y]];i<=y;i++)if(a[i]+tag[pos[i]]<k)sum++;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)
{
int w=lower_bound(b+l[i],b+1+r[i],k-tag[i])-b;
sum+=(w-l[i]);
}
return sum;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
b[i]=a[i];
pos[i]=(i-1)/len+1;
}
for(register int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
for(register int i=1;i<=cnt;i++)
sort(b+l[i],b+1+r[i]);
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)
{
add(x,y,c);
for(register int i=l[pos[x]];i<=r[pos[x]];i++)b[i]=a[i];
for(register int i=l[pos[y]];i<=r[pos[y]];i++)b[i]=a[i];
sort(b+l[pos[x]],b+1+r[pos[x]]);
sort(b+l[pos[y]],b+1+r[pos[y]]);
}
if(op==1)printf("%lld\n",ask(x,y,c*c));
}
return 0;
}
3. 数列分块入门3
区间修改,询问区间中 \(c\) 的前驱
和上一题差不多,同样也是块内排序,二分查找
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],b[MAXN],tag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
if(x>y)return;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
a[i]+=k;
return;
}
for(register int i=x;i<=r[pos[x]];i++)a[i]+=k;
for(register int i=l[pos[y]];i<=y;i++)a[i]+=k;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
inline int ask(int x,int y,int k)
{
int ans=-1;
if(x>y)return ans;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
if(a[i]+tag[pos[i]]<k)ans=max(ans,a[i]+tag[pos[i]]);
return ans;
}
for(register int i=x;i<=r[pos[x]];i++)if(a[i]+tag[pos[i]]<k)ans=max(ans,a[i]+tag[pos[i]]);
for(register int i=l[pos[y]];i<=y;i++)if(a[i]+tag[pos[i]]<k)ans=max(ans,a[i]+tag[pos[i]]);
for(register int i=pos[x]+1;i<=pos[y]-1;i++)
{
int w=lower_bound(b+l[i],b+1+r[i],k-tag[i])-b;
w--;
if(b[w]+tag[pos[w]]<k)ans=max(ans,b[w]+tag[pos[w]]);
}
return ans;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
b[i]=a[i];
pos[i]=(i-1)/len+1;
}
for(register int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
for(register int i=1;i<=cnt;i++)
sort(b+l[i],b+1+r[i]);
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)
{
add(x,y,c);
for(register int i=l[pos[x]];i<=r[pos[x]];i++)b[i]=a[i];
for(register int i=l[pos[y]];i<=r[pos[y]];i++)b[i]=a[i];
sort(b+l[pos[x]],b+1+r[pos[x]]);
sort(b+l[pos[y]],b+1+r[pos[y]]);
}
if(op==1)printf("%lld\n",ask(x,y,c));
}
return 0;
}
4. 数列分块入门4
区间修改,区间查询
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],tag[MAXN],t[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void add(int x,int y,int k)
{
if(x>y)return;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
{
a[i]+=k;
t[pos[i]]+=k;
}
return;
}
for(register int i=x;i<=r[pos[x]];i++)a[i]+=k,t[pos[i]]+=k;
for(register int i=l[pos[y]];i<=y;i++)a[i]+=k,t[pos[i]]+=k;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]+=k;
}
inline int ask(int x,int y,int k)
{
int sum=0;
if(x>y)return 0;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
sum=((sum+a[i])%k+tag[pos[i]])%k;
return sum;
}
for(register int i=x;i<=r[pos[x]];i++)sum=((sum+a[i])%k+tag[pos[i]])%k;
for(register int i=l[pos[y]];i<=y;i++)sum=((sum+a[i])%k+tag[pos[i]])%k;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)sum=((sum+t[i])%k+((r[i]-l[i]+1)%k)*tag[i]%k)%k;
return sum;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/len+1;
t[pos[i]]+=a[i];
}
for(register int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)add(x,y,c);
if(op==1)printf("%lld\n",ask(x,y,c+1));
}
return 0;
}
5. 数列分块入门5
区间开方,区间求和
记录当前块中最大值,当最大值为 \(1\) 时不需要进行开方操作
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],maxn[MAXN],t[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void change(int x,int y)
{
if(x>y)return;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
{
t[pos[i]]-=a[i];
a[i]=sqrt(a[i]);
t[pos[i]]+=a[i];
}
return;
}
for(register int i=x;i<=r[pos[x]];i++)t[pos[i]]-=a[i],a[i]=sqrt(a[i]),t[pos[i]]+=a[i];
for(register int i=l[pos[y]];i<=y;i++)t[pos[i]]-=a[i],a[i]=sqrt(a[i]),t[pos[i]]+=a[i];
for(register int i=pos[x]+1;i<=pos[y]-1;i++)
{
if(maxn[i]==1)continue;
maxn[i]=sqrt(maxn[i]);
for(register int j=l[i];j<=r[i];j++)
{
t[i]-=a[j];
a[j]=sqrt(a[j]);
t[i]+=a[j];
}
}
}
inline int ask(int x,int y)
{
int sum=0;
if(x>y)return 0;
if(pos[x]==pos[y])
{
for(register int i=x;i<=y;i++)
sum+=a[i];
return sum;
}
for(register int i=x;i<=r[pos[x]];i++)sum+=a[i];
for(register int i=l[pos[y]];i<=y;i++)sum+=a[i];
for(register int i=pos[x]+1;i<=pos[y]-1;i++)sum+=t[i];
return sum;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/len+1;
t[pos[i]]+=a[i];
maxn[pos[i]]=max(maxn[pos[i]],a[i]);
}
for(register int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)
{
change(x,y);
if(maxn[pos[x]]!=1)
{
maxn[pos[x]]=0;
for(register int i=l[pos[x]];i<=r[pos[x]];i++)maxn[pos[x]]=max(maxn[pos[x]],a[i]);
}
if(maxn[pos[y]]!=1)
{
maxn[pos[y]]=0;
for(register int i=l[pos[y]];i<=r[pos[y]];i++)maxn[pos[y]]=max(maxn[pos[y]],a[i]);
}
}
if(op==1)printf("%lld\n",ask(x,y));
}
return 0;
}
6. 数列分块入门6
单点插入,单点查询
因为数据随机所以不需要重构块
点击查看代码
#include<bits/stdc++.h>
#define pii pair<int,int>
#define mp make_pair
#define int long long
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],pos[MAXN];
vector<int>v[1005];
inline pii ask(int x)
{
int k=1;
while(x>v[k].size())
{
x-=v[k].size();
k++;
}
return mp(k,x-1);
}
inline void add(int x,int k)
{
pii now=ask(x);
v[now.first].insert(v[now.first].begin()+now.second,k);
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/len+1;
v[pos[i]].push_back(a[i]);
}
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)add(x,y);
if(op==1)
{
pii now=ask(y);
printf("%lld\n",v[now.first][now.second]);
}
}
return 0;
}
7. 数列分块入门7
区间加乘,单点查询
重点是边上的散块,加或乘的时候直接将所有标记下传即可
点击查看代码
#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+5;
const int MOD=10007;
int n,len,cnt;
int a[MAXN],tag[MAXN],mul[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void pushdown(int p)
{
for(register int i=l[p];i<=r[p];i++)
a[i]=(a[i]*mul[p]%MOD+tag[p])%MOD;
mul[p]=1;
tag[p]=0;
}
inline void add(int x,int y,int k)
{
if(x>y)return;
if(pos[x]==pos[y])
{
pushdown(pos[x]);
for(register int i=x;i<=y;i++)
a[i]=(a[i]+k)%MOD;
return;
}
pushdown(pos[x]);
pushdown(pos[y]);
for(register int i=x;i<=r[pos[x]];i++)a[i]=(a[i]+k)%MOD;
for(register int i=l[pos[y]];i<=y;i++)a[i]=(a[i]+k)%MOD;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]=(tag[i]+k)%MOD;
}
inline void mult(int x,int y,int k)
{
if(x>y)return;
if(pos[x]==pos[y])
{
pushdown(pos[x]);
for(register int i=x;i<=y;i++)
a[i]=(a[i]*k)%MOD;
return;
}
pushdown(pos[x]);
pushdown(pos[y]);
for(register int i=x;i<=r[pos[x]];i++)a[i]=(a[i]*k)%MOD;
for(register int i=l[pos[y]];i<=y;i++)a[i]=(a[i]*k)%MOD;
for(register int i=pos[x]+1;i<=pos[y]-1;i++)tag[i]=(tag[i]*k)%MOD,mul[i]=(mul[i]*k)%MOD;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/len+1;
}
for(register int i=1;i<=cnt;i++)
{
mul[i]=1,tag[i]=0;
l[i]=(i-1)*len+1;
r[i]=i*len;
}
r[cnt]=n;
for(register int i=1;i<=n;i++)
{
int op,x,y,c;
scanf("%lld%lld%lld%lld",&op,&x,&y,&c);
if(op==0)add(x,y,c);
if(op==1)mult(x,y,c);
if(op==2)printf("%lld\n",(a[y]*mul[pos[y]]%MOD+tag[pos[y]])%MOD);
}
return 0;
}
8. 数列分块入门8
区间求特定数的个数+覆盖
可以记录一个当前块中的元素是否相等的标记,如果作为散块被访问的话就先下传标记。
点击查看代码
#include<bits/stdc++.h>
#define int long long
#define INF 0x7f7f7f7f7f7f7f7f
using namespace std;
const int MAXN=1e5+5;
int n,len,cnt;
int a[MAXN],flag[MAXN];
int pos[MAXN],l[MAXN],r[MAXN];
inline void pushdown(int k)
{
if(flag[k]==INF)return;
for(register int i=l[k];i<=r[k];i++)
a[i]=flag[k];
flag[k]=INF;
}
inline int change(int x,int y,int k)
{
int sum=0;
if(x>y)return 0;
if(pos[x]==pos[y])
{
pushdown(pos[x]);
for(register int i=x;i<=y;i++)
{
if(a[i]!=k)a[i]=k;
else sum++;
}
return sum;
}
pushdown(pos[x]);
pushdown(pos[y]);
for(register int i=x;i<=r[pos[x]];i++)
{
if(a[i]!=k)a[i]=k;
else sum++;
}
for(register int i=l[pos[y]];i<=y;i++)
{
if(a[i]!=k)a[i]=k;
else sum++;
}
for(register int i=pos[x]+1;i<=pos[y]-1;i++)
{
if(flag[i]==k)sum+=(r[i]-l[i]+1);
else if(flag[i]!=INF)flag[i]=k;
else
{
flag[i]=k;
for(register int j=l[i];j<=r[i];j++)
{
if(a[j]==k)sum++;
else a[j]=k;
}
}
}
return sum;
}
signed main()
{
scanf("%lld",&n);
len=sqrt(n);
cnt=(n/len)+(n%len!=0);
for(register int i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
pos[i]=(i-1)/len+1;
}
for(register int i=1;i<=cnt;i++)
{
l[i]=(i-1)*len+1;
r[i]=i*len;
for(register int j=l[i]+1;j<=r[i];j++)
if(a[j]!=a[j-1])
{
flag[i]=INF;
break;
}
if(flag[i]!=INF)flag[i]=a[l[i]];
}
r[cnt]=n;
for(register int i=1;i<=n;i++)
{
int x,y,c;
scanf("%lld%lld%lld",&x,&y,&c);
printf("%lld\n",change(x,y,c));
}
return 0;
}

浙公网安备 33010602011771号