分块题目训练:
1.逆序对——求区间最小值
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,单点查值。
//给定一个长为n的序列,以及n个操作,操作涉及区间加法,单点查值
#include
using namespace std;
const int maxn = 50086;
int a[maxn],n,m;
int add[maxn];
int pos[maxn];//,sum[maxn];
int L[maxn],R[maxn];
inline void change(int l,int r,int d){
int p = pos[l], q = pos[r];
if(p==q)//如果要修改的区间在同一个块里面
for(int i=l;i<=r;++i) a[i]+=d;
else {//如果要修改的区间不在同一个块里面
for(int i=p+1;i<=q-1;++i) add[i] += d;//直接给块赋值
for(int i=l;i<=R[p];i++) a[i]+=d;
for(int i=L[q];i<=r;++i) a[i]+=d;
}
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>a[i];
int t = sqrt(n);//分为t块
for(int i=1;i<=t;++i){//开始分块,完整块
L[i] = (i-1)*t + 1;
R[i] = i*t;
}
if(R[t]>opt;
if(!opt) {
cin>>l>>r>>c;
change(l,r,c);
}
else {
cin>>r;
int q = pos[r];
cout<
给出一个长为 n 的数列,以及 m个操作,操作涉及区间加法,询问区间内小于某个值 x 的元素个数。
#include
using namespace std;
typedef long long ll;
const int maxn = 5e4+100;
int n,m,blo;
int v[maxn],bl[maxn];
int atag[maxn];//块的增量
vectorve[505];//存放每一块的元素
void reset(int x){//将第x块的元素重新排序
ve[x].clear();
for(int i=(x-1)*blo+1;i<=min(x*blo,n);++i)
ve[x].push_back(v[i]);
sort(ve[x].begin(),ve[x].end());
}
void add(int a,int b,int c){//区间更新
for(int i=a;i<=min(bl[a]*blo,b);++i) v[i]+=c;
reset(bl[a]);
if(bl[a]!=bl[b]){
for(int i=(bl[b]-1)*blo+1;i<=b;++i) v[i]+=c;
reset(bl[b]);
}
for(int i=bl[a]+1;i<=bl[b]-1;++i) atag[i] += c;
}
int query(int a,int b,int c){
int ans = 0;
//对于不完整的块直接暴力判断
for(int i=a;i<=min(bl[a]*blo,b);i++){
if(v[i]+atag[bl[a]]>n>>m;
blo = sqrt(n);
for(int i=1;i<=n;++i) cin>>v[i];
for(int i=1;i<=n;++i){
bl[i] = (i-1)/blo + 1;
ve[bl[i]].push_back(v[i]);
}
for(int i=1;i<=bl[n];++i){
sort(ve[i].begin(),ve[i].end());//将每一块的元素排序
}
for(int i=1;i<=m;++i){
int opt,l,r,d;
cin>>opt>>l>>r>>d;
if(opt==1) add(l,r,d);
else printf("%d\n",query(l,r,d));
}
}
/*10 5
1 2 3 4 5 6 7 8 9 10
1 1 10 1
2 1 10 3
1 1 5 3
1 6 10 4
2 1 10 15
*/
给出一个长为 n 的数列,以及 n 个操作,操作涉及区间加法,询问区间内小于某个值 x 的前驱(比其小的最大元素)。
set会比vector快一点。set插入时间复杂度是o(1),并且set对于删除和插入元素更加灵活。
vector写法:
#include
using namespace std;
const int maxn = 5e4+100;
int n,m,blo;
int val[maxn],bl[maxn];
int add[maxn];
vectorve[510];
void reset(int x){
ve[x].clear();
for(int i=(bl[x]-1)*blo+1;i<=bl[x]*blo;i++) ve[x].push_back(val[i]);
sort(ve[x].begin(),ve[x].end());
}
void change(int l,int r,int d){
for(int i=l;i<=min(r,bl[l]*blo);++i) val[i]+=d;
reset(bl[l]);
if(bl[l]!=bl[r]){
for(int i=(bl[r]-1)*blo+1;i<=r;i++) val[i]+=d;
reset(bl[r]);
}
for(int i=bl[l]+1;i<=bl[r]-1;i++) add[i]+=d;
}
int query(int l,int r,int d){
int mx = -1;
for(int i=l;i<=min(r,bl[l]*blo);++i)
if(val[i]+add[bl[l]]>n>>m;blo=sqrt(n);
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<=n;i++){
bl[i] = (i-1)/blo + 1;
ve[bl[i]].push_back(val[i]);
}
for(int i=1;i<=bl[n];i++){
sort(ve[i].begin(),ve[i].end());
}
for(int i=1;i<=m;i++){
int opt,l,r,d;
cin>>opt>>l>>r>>d;
if(opt==1) change(l,r,d);
else printf("%d\n",query(l,r,d));
}
}
set写法:
#include
using namespace std;
const int N = 1e5+10;
int val[N],n,m,blo;
int add[N],bl[N];
setst[550];
void change(int l,int r,int d){
for(int i=l;i<=min(r,bl[l]*blo);i++){
st[bl[l]].erase(val[i]);
val[i]+=d;
st[bl[l]].insert(val[i]);
}
if(bl[l]!=bl[r]){
for(int i=(bl[r]-1)*blo+1;i<=r;i++){
st[bl[r]].erase(val[i]);
val[i]+=d;
st[bl[r]].erase(val[i]);
}
}
for(int i=bl[l]+1;i<=bl[r]-1;i++) add[i]+=d;
}
int query(int l,int r,int d){
int ans = -1;
for(int i=l;i<=min(r,bl[l]*blo);i++){
if(val[i]+add[bl[i]]::iterator it = st[i].lower_bound(x);
if(it==st[i].begin()) continue;
--it;
ans = max(ans,*it+add[i]);
}
return ans;
}
int main()
{
cin>>n>>m;
blo = sqrt(n);
for(int i=1;i<=n;++i) cin>>val[i];
for(int i=1;i<=n;++i){
bl[i] = (i-1)/blo + 1;
st[bl[i]].insert(val[i]);
}
for(int i=1;i<=m;i++){
int opt,l,r,d;
cin>>opt>>l>>r>>d;
if(opt==1) change(l,r,d);
else printf("%d\n",query(l,r,d));
}
}
给出一个长为n的数列,以及m个操作,操作涉及区间加法,区间求和。
#include
using namespace std;
const int N = 1e5+100;
int val[N],L[N],R[N],pos[N],sum[N],add[N];
int n,m;
inline void change(int l,int r,int d){
int p = pos[l] ,q = pos[r];
if(p==q) for(int i=l;i<=r;i++) val[i]+=d,sum[p]+=d;
else {
for(int i=l;i<=R[p];i++) val[i]+=d,sum[p]+=d;
for(int i=L[q];i<=r;i++) val[i]+=d,sum[q]+=d;
for(int i=p+1;i<=q-1;i++) add[i]+=d;
}
}
inline int query(int l,int r){
int p = pos[l] , q = pos[r];
int ans = 0;
if(p==q) for(int i=l;i<=r;i++) ans += val[i]+add[i];
else {
for(int i=p+1;i<=q-1;i++) ans+=sum[i]+(R[i]-L[i]+1)*add[i];
for(int i=l;i<=R[p];i++) ans += val[i]+add[p];
for(int i=L[q];i<=r;i++) ans += val[i]+add[q];
}
return ans;
}
int main()
{
cin>>n>>m;
int t = sqrt(n);
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<=t;i++){
L[i] = (i-1)*t+1;
R[i] = i*t;
}
if(R[t]>opt;
if(opt==1) {
cin>>l>>r>>d;
change(l,r,d);
}
else {
cin>>l>>r;
printf("%d\n",query(l,r));
}
}
}
给出一个长为n的数列,以及n个操作,操作涉及区间开方,区间求和。
对于区间开方,我们可以想到,对于一个区间的数,经过数次开方后,他们会变为0或1,所以采取一种分块优化的暴力做法,只要每个整块暴力开方后,记录一下元素是否都变成了 0 / 1,区间修改时跳过那些全为 0 / 1 的块即可。
#include
using namespace std;
const int N = 1e5 + 100;
int n,m,blo;
int val[N],bl[N],sum[N],vis[N];
void solve_sqrt(int x){//暴力判断这一块是否变成了1 或 0
if(vis[x]) return;
vis[x]=1;
sum[x]=0;
for(int i=(x-1)*blo+1;i<=x*blo;i++){
val[i] = sqrt(val[i]);
sum[x]+=val[i];
if(val[i]>1) vis[x]=0;
}
}
void change(int l,int r){
for(int i=l;i<=min(r,bl[l]*blo);i++){
sum[bl[l]]-=val[i];
val[i] = sqrt(val[i]);
sum[bl[l]]+=val[i];
}
if(bl[l]!=bl[r]){
for(int i=(bl[r]-1)*blo+1;i<=r;i++){
sum[bl[r]]-=val[i];
val[i] = sqrt(val[i]);
sum[bl[r]]+=val[i];
}
}
for(int i=bl[l]+1;i<=bl[r]-1;i++)
solve_sqrt(i);
}
int query(int l,int r){
int ans = 0;
for(int i=l;i<=min(bl[l]*blo,r);i++) ans+=val[i];
if(bl[l]!=bl[r])
for(int i=(bl[r]-1)*blo+1;i<=r;i++) ans+=val[i];
for(int i=bl[l]+1;i<=bl[r]-1;i++) ans+=sum[i];
return ans;
}
int main()
{
cin>>n>>m;
blo =sqrt(n);
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<=n;i++){
bl[i]=(i-1)/blo + 1;
sum[bl[i]]+=val[i];
}
for(int i=1;i<=m;i++){
int opt,l,r;
cin>>opt>>l>>r;
if(opt==1) change(l,r);
else printf("%d\n",query(l,r));
}
}
分块入门6:
给出一个长为 n 的数列,以及 n 个操作,操作涉及单点插入,单点询问,数据随机生成。
#include
using namespace std;
const int maxn = 1e5+100;
int val[maxn],s[maxn];
int n,m,blo,q;
struct node{
int s,t;
};
vectore[1010];
inline node query(int x){//返回某一元素在块中的位置
int i=1;
while(x>(int)e[i].size())
x-=(int)e[i].size(),i++;
return node{i,x-1};
}
inline void rebuild(){//重新分块
int top=0;
for(int i=1;i<=q;++i){
for(int j=0;j20*blo)//此块元素过多,重新分块
rebuild();
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;++i)
cin>>val[i];
blo = sqrt(n);q=(n-1)/blo+1;
for(int i=1;i<=n;i++)
e[(i-1)/blo+1].push_back(val[i]);//分块
for(int i=1;i<=m;i++){
int opt,l,r;
cin>>opt;
if(opt==1){
cin>>l>>r;
change(l,r);
}
else {
cin>>r;
node x = query(r);
cout<
给出一个长为n的数列,以及n个操作,操作涉及区间乘法,区间加法,单点询问。
#include
using namespace std;
const int maxn = 1e5+100;
int val[maxn],bl[1000],add[1000],mul[1000],sum[1000];
int n,m,blo;
void resert(int x){
for(int i=(bl[x]-1)*blo+1;i<=min(n,bl[x]*blo);i++)
val[i] = val[i]*mul[x]+add[x];
mul[x]=1,add[x]=0;
}
void change(int l,int r,int d,int opt){
resert(bl[l]);
for(int i=l;i<=min(r,bl[l]*blo);i++){
if(opt==1) val[i]+=d;
else val[i]*=d;
}
if(bl[l]!=bl[r]){
resert(bl[r]);
for(int i=(bl[r]-1)*blo+1;i<=r;i++){
if(opt==1) val[i]+=d;
else val[i]*=d;
}
}
for(int i=bl[l]+1;i<=bl[r]-1;i++){
if(opt==1) add[i]+=d;
else {
add[i]*=d;
mul[i]*=d;
}
}
}
int main()
{
for(int i=1;i<=1000;i++) mul[i]=1;
cin>>n>>m;
blo = sqrt(n);
for(int i=1;i<=n;i++) {
cin>>val[i];
bl[i]=(i-1)/blo+1;
sum[bl[i]]+=val[i];
}
for(int i=1;i<=m;i++){
int opt,l,r,d;
cin>>opt;
if(opt==1||opt==2){//加法
cin>>l>>r>>d;
change(l,r,d,opt);
}
else {
cin>>r;
cout<
给出一个长为n的数列,以及n个操作,操作涉及区间询问等于一个数c的元素,并将这个区间的所有元素改为c。
#include
using namespace std;
const int N = 1e5+100;
int val[N],vis[1000],bl[N];
int q,n,m,blo;
void reset(int x){
if(vis[x]==-1) return;
for(int i=(x-1)*blo+1;i<=blo*x;++i)
val[i]=vis[x];
vis[x]=-1;
}
int query(int l,int r,int d){
int ans = 0;
reset(bl[l]);
for(int i=l;i<=min(r,bl[l]*blo);++i) {
if(val[i]==d) ans++;
val[i]=d;
}
if(bl[l]!=bl[r]){
reset(bl[r]);
for(int i=(bl[r]-1)*blo+1;i<=r;++i) {
if(val[i]==d) ans++;
val[i]=d;
}
}
for(int i=bl[l]+1;i<=bl[r]-1;++i){
if(vis[i]!=-1){
if(vis[i]!=d) vis[i]=d;
else ans+=blo;
}
else {
for(int j=(i-1)*blo+1;j<=i*blo;j++)
if(val[j]!=d) val[j]=d;
else ans++;
vis[i]=d;
}
}
return ans;
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) cin>>val[i];
for(int i=1;i<=1000;i++) vis[i]=-1;
blo = sqrt(n);
for(int i=1;i<=n;++i) bl[i] = (i-1)/blo+1;
for(int i=1;i<=m;i++){
int l,r,d;
cin>>l>>r>>d;
printf("%d\n",query(l,r,d));
}
}