分块专题四题题解(hzwer专题)

hzwer博客九题详解:http://hzwer.com/8053.html

分块专题题目地址:https://loj.ac/problems/search?keyword=%E5%88%86%E5%9D%97

总结:最近在刷算法竞赛的时候看到了这个专题,就刷了前面几道题练习练习分块的写法。

         首先是读入数组和数组分块的操作

    n=read();
    m = sqrt(n);
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)block[i]=(i-1)/m+1;

         然后是区间加分,其中ad数组用来标记整个区间的增值

void add(int l,int r,int x)
{
    for(int i=l;i<=min(block[l]*m,r);i++)a[i]+=x;
    if(block[l]!=block[r])
    {
	for(int i=(block[r]-1)*m+1;i<=r;i++)a[i]+=x;
    }
    for(int i=block[l]+1;i<=block[r]-1;i++)ad[i]+=x;
}

分块1:区间加法,单点查值,分块的模板。

#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int maxn = 50005;
#define ll long long
int n,m,a[maxn],block[maxn],ad[maxn];
ll read()
{
    ll x=0,f=1;char ch=getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
void add(int l,int r,int x)
{
    for(int i=l;i<=min(block[l]*m,r);i++)a[i]+=x;
    if(block[l]!=block[r])
    {
	for(int i=(block[r]-1)*m+1;i<=r;i++)a[i]+=x;
    }
    for(int i=block[l]+1;i<=block[r]-1;i++)ad[i]+=x;
}
int main() {
    n=read();
    m = sqrt(n);
    for(int i=1;i<=n;i++)a[i]=read();
    for(int i=1;i<=n;i++)block[i]=(i-1)/m+1;
    for(int i=1;i<=n;i++)
    {
	int x,y,z,k;
	x=read();
	y=read();
	z=read();
	k=read();
	if(x==0)add(y,z,k);
	else printf("%d\n",a[z]+ad[block[z]]);
    }
    return 0;
}

分块2:区间加法,询问区间内小于一个值的个数,建区间和添加的时候将区间排序,查询的时候区间前面和最后都暴力即可,在整个区间内进行二分查找该值的位置。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <vector>
using namespace std;
const int maxn = 50005;
#define _for(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
ll read()
{
    ll x=0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch = getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,a[maxn],block[maxn],add[maxn];
vector<int> Q[maxn];
void getsort(int x)
{
    Q[x].clear();
    _for(i,(x-1)*m+1,x*m)Q[x].push_back(a[i]);
    sort(Q[x].begin(),Q[x].end());
}
void addnum(int l,int r,int x)
{
    _for(i,l,min(block[l]*m,r))a[i]+=x;
    getsort(block[l]);
    if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)a[i]+=x;
    getsort(block[r]);
    _for(i,block[l]+1,block[r]-1)add[i]+=x;
}
int query(int l,int r,int x)
{
    int sum = 0;
    _for(i,l,min(block[l]*m,r))if(a[i]+add[block[i]]<x)sum++;
    if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)if(a[i]+add[block[i]]<x)sum++;
    _for(i,block[l]+1,block[r]-1)sum+=lower_bound(Q[i].begin(),Q[i].end(),x-add[i])-Q[i].begin();
    return sum;
}
int main(int argc, char const *argv[])
{
    n = read();
    m = sqrt(n);
    _for(i,1,n)a[i]=read();
    _for(i,1,n)
    {
        block[i]=(i-1)/m+1;
        Q[block[i]].push_back(a[i]);
    }
    _for(i,1,(n-1)/m+1)
    {
        sort(Q[i].begin(),Q[i].end());
    }
    _for(i,1,n)
    {
        int x,l,r,k;
        x=read();
        l=read();
        r=read();
        k=read();
        if(x==0)addnum(l,r,k);
        else
        {
            int num = k*k;
            printf("%d\n",query(l,r,num));
        }
    }
    return 0;
}

分块3:区间加法,询问区间内小于x的最大值。与第二题类似,还是排序查找即可,不完整区间暴力,区间二分,最后找其中的最大值;也可以在分块内使用set自动排序。该题sqrt分块会T,得分100。。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
const int maxn = 100005;
#define _for(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
ll read()
{
    ll x=0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch = getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,a[maxn],block[maxn],add[maxn];
set<int> Q[105];
void addnum(int l,int r,int x)
{
    _for(i,l,min(r,block[l]*m))
    {
        Q[block[l]].erase(a[i]);
        a[i]+=x;
        Q[block[l]].insert(a[i]);
    }
    if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)
    {
        Q[block[r]].erase(a[i]);
        a[i]+=x;
        Q[block[r]].insert(a[i]);
    }
    _for(i,block[l]+1,block[r]-1)add[i]+=x;
}
int query(int l,int r,int x)
{
    int ans = -1;
    _for(i,l,min(block[l]*m,r))if(a[i]+add[block[l]]<x)ans=max(ans,a[i]+add[block[l]]);
    if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)if(a[i]+add[block[r]]<x)ans=max(ans,a[i]+add[block[r]]);
    _for(i,block[l]+1,block[r]-1)
    {
        set<int>::iterator it = Q[i].lower_bound(x-add[i]);
        if(it==Q[i].begin())continue;
        --it;
        ans = max(ans,*it+add[i]);
    }
    return ans;
}
int main()
{
    n=read();
    m=1500;
    _for(i,1,n)a[i]=read();
    _for(i,1,n)
    {
        block[i]=(i-1)/m+1;
        Q[block[i]].insert(a[i]);
    }
    _for(i,1,n)
    {
        int x,y,z,k;
        x=read(),y=read(),z=read(),k=read();
        if(x==0)addnum(y,z,k);
        else printf("%d\n",query(y,z,k));
    }
}

分块4:区间加法,查询区间和。记录每个区间和,在更新的时候重新记录区间和与标记区间。查询的时候与第一题类似。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cstdio>
#include <cmath>
#include <set>
#include <vector>
using namespace std;
const int maxn = 50005;
#define _for(i,a,b) for(int i=a;i<=b;i++)
#define ll long long
ll read()
{
    ll x=0,f=1;char ch = getchar();
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch = getchar();}
    while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
    return x*f;
}
int n,m,block[maxn];
ll a[maxn],add[maxn],sum[maxn];
void addnum(int l,int r,int x)
{
    _for(i,l,min(r,block[l]*m))
    {
        a[i]+=x;
        sum[block[l]]+=x;
    }
    if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)
    {
        a[i]+=x;
        sum[block[r]]+=x;
    }
    _for(i,block[l]+1,block[r]-1)add[i]+=x;
}
ll query(int l,int r)
{
    ll ans = 0;
    _for(i,l,min(r,block[l]*m))ans=(ans+a[i]+add[block[l]]);
    if(block[l]!=block[r])_for(i,(block[r]-1)*m+1,r)ans=(ans+a[i]+add[block[r]]);
    _for(i,block[l]+1,block[r]-1)
    {
        ans=ans+add[i]*m+sum[i];
    }
    return ans;
}
int main(int argc, char const *argv[])
{
    n=read();
    m=sqrt(n);
    _for(i,1,n)a[i]=read();
    _for(i,1,n)
    {
        block[i]=(i-1)/m+1;
        sum[block[i]]+=a[i];
    }
    _for(i,1,n)
    {
        int x,y,z,k;
        x=read(),y=read(),z=read(),k=read();
        if(x==0)addnum(y,z,k);
        else printf("%lld\n",query(y,z)%(k+1));
    }
    return 0;
}


  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值