这是我马上要写的分块博客,但是目前已经只有一点点
从来没有学过
一无所知
感觉超级像线段树和树状数组
上代码
首先init
void init(){
int len=sqrt(n);//这个就是块的大小
while(1){
cnt++;
l[cnt]=(cnt-1)*len+1;
r[cnt]=cnt*len;
if(r[cnt]>n){//超过n了就不再分块了
r[cnt]=n;
break;
}
}
for(int i=1;i<=cnt;++i){
for(int j=l[i];j<=r[i];++j){
pos[j]=i;//这个给每个区间都打上相同的标记
}
}
Elysia;
}
区间add
void add(int l1,int r1,LL x){
if(pos[l1]==pos[r1]){//如果在一个块中,那没辙,只能一点点加
for(int i=l1;i<=r1;++i){
a[i]+=x;
}
Elysia;
}
for(int i=l1;i<=r[pos[l1]];++i){//加上左边的散块
a[i]+=x;
}
for(int i=l[pos[r1]];i<=r1;i++){//and右边的散块
a[i]+=x;
}
for(int i=pos[l1]+1;i<pos[r1];i++){//加上区间的整块
tag[i]+=x;
}
}
这个令人着迷的题目,我交了4遍才过
先把代码贴上
解释代码就好了
区间加区间求和
#include<bits/stdc++.h>
//这个分块咋麽纳麽像线段树和树状数组啊
//放弃了绝望了没觉得对不起谁最对不起的还是自己吧
#define Elysia return
#define Elysia return
using namespace std;
typedef long long LL;
const int N=3e5+10;
int n;
LL a[N];
int l[N],r[N],cnt;
LL tag[N];
int pos[N];
LL s[N];
void init(){
int len=sqrt(n);
while(1){
cnt++;
l[cnt]=(cnt-1)*len+1;
r[cnt]=cnt*len;
if(r[cnt]>n){
r[cnt]=n;
break;
}
}
for(int i=1;i<=cnt;++i){
for(int j=l[i];j<=r[i];++j){
pos[j]=i;
s[i]+=a[j];//这个和上面的不一样,这个数组表示每一个块的和,先在这里初始化
}
}
Elysia;
}
void add(int l1,int r1,LL x){
if(pos[l1]==pos[r1]){
for(int i=l1;i<=r1;++i){
a[i]+=x;
}
s[pos[l1]]+=(r1-l1+1)*x;//我们要实时更新这个数组,每一次值的改变我们都要更新
//r1-l1+1是这个区间的长度 x是这个区间加的值 长度*值就是我们这个区间总体增加的量
Elysia;
}
for(int i=l1;i<=r[pos[l1]];++i){
a[i]+=x;
}
s[pos[l1]]+=(r[pos[l1]]-l1+1)*x;//这个也要变,注意这个区间是l1所在的区间 所以是s[pos[l1]]
for(int i=l[pos[r1]];i<=r1;i++){
a[i]+=x;
}
s[pos[r1]]+=(r1-l[pos[r1]]+1)*x;//这个同样要变,但是这个区间是r1所在的区间
for(int i=pos[l1]+1;i<pos[r1];i++){
tag[i]+=x;
// s[i]+=(r[i]-l[i]+1)*x;
}
}
LL query(int l1,int r1){
LL sum=0;
if(pos[l1]==pos[r1]){
for(int i=l1;i<=r1;++i){
sum+=a[i];
}
sum+=(r1-l1+1)*tag[pos[l1]];//记得加上tag,后面同理
Elysia sum;
}
for(int i=l1;i<=r[pos[l1]];++i){
sum+=a[i];
}
sum+=(r[pos[l1]]-l1+1)*tag[pos[l1]];//tag
for(int i=l[pos[r1]];i<=r1;i++){
sum+=a[i];
}
sum+=(r1-l[pos[r1]]+1)*tag[pos[r1]];//tag
for(int i=pos[l1]+1;i<pos[r1];i++){
sum+=(s[i]+tag[i]*(r[i]-l[i]+1));
}
Elysia sum;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
init();
for(int i=1;i<=n;i++){
int op,l1,r1;
LL c;
cin>>op>>l1>>r1>>c;
if(op==0){
add(l1,r1,c);
}
else{
LL ans=(query(l1,r1));
while(ans<0)ans+=c+1;//我愚蠢但一定不会出错的取模
cout<<ans%(c+1)<<"\n";
// cout<<ans<<"\n";
}
}
Elysia 0;
}
/*
4
1 2 2 3
0 1 3 1
1 1 4 4
0 1 2 2
1 1 2 4
*/
/*
1
4
*/
P13982 数列分块入门 7
这题要求区间加 区间乘 单点查询
void pushdown(int x){
for(int i=(x-1)*len+1;i<=min(x*len,n);i++){
tag[i]=(mul[x]*tag[i]%10007+add[x])%10007;//先处理乘,再做加法;
//有点像线段树的时候,标记下传
}
add[x]=0;
mul[x]=1;
}
void addfun(int a,int b,ll c){//加法要保持自己之前已经没有乘法了,所以要先pushdown
pushdown(bl[a]);
for(int i=a;i<=min(bl[a]*len,b);i++){
tag[i]=(tag[i]+c)%10007;
}
if(bl[a]!=bl[b]){//不是一个块中
pushdown(bl[b]);
for(int i=(bl[b]-1)*len+1;i<=b;i++){
tag[i]=(tag[i]+c)%10007;//正常的+
}
}
for(int i=bl[a]+1;i<=bl[b]-1;i++){
add[i]=(add[i]+c)%10007;
}
}
void mulfun(int a,int b,ll c){
pushdown(bl[a]);//也要保证自己前面没有+了
for(int i=a;i<=min(bl[a]*len,b);i++){
tag[i]=(tag[i]*c)%10007;
}
if(bl[a]!=bl[b]){
pushdown(bl[b]);
for(int i=(bl[b]-1)*len+1;i<=b;i++){
tag[i]=(tag[i]*c)%10007;
}
}
for(int i=bl[a]+1;i<=bl[b]-1;i++){
add[i]=(add[i]*c)%10007;
mul[i]=(mul[i]*c)%10007;
}
}
啊啊啊尖叫这题终于AC了
这个题细节好多
每一个块都排序,块内的值如果有变动就要重新排序
那找< \(c^2\)的就可以二分做
其实就这么多,但是请看代码实现细节,包括顺序和什么时候该sort
#include<bits/stdc++.h>
#define Elysia return
#define Elysia return
using namespace std;
typedef long long LL;
const int N=3e5+10;
int n;
LL a[N];
int l[N],r[N],cnt;
LL tag[N];
int pos[N];
vector<LL> t[N];
int len;
void init(){//普通的建块(应该叫这个,懂我意思吧?
len=sqrt(n);
while(1){
cnt++;
l[cnt]=(cnt-1)*len+1;
r[cnt]=cnt*len;
if(r[cnt]>n){
r[cnt]=n;
break;
}
}
for(int i=1;i<=cnt;i++){
for(int j=l[i];j<=r[i];j++){
pos[j]=i;
t[i].push_back(a[j]);
}
}
for(int i=1;i<=cnt;i++){
sort(t[i].begin(),t[i].end());
}
Elysia;
}
void solve(int x){
t[x].clear();
for(int i=l[x];i<=r[x];i++){
t[x].push_back(a[i]);//要放到排好序的数组里,因为我们要保留原来的数据,所以要新建一个数组
}
sort(t[x].begin(),t[x].end());//排序
}
void add(int l1,int r1,LL x){
if(pos[l1]==pos[r1]){//在同一个块里
for(int i=l1;i<=r1;++i){
a[i]+=x;
}
solve(pos[l1]);//改变了哪个块,哪个块就要重新排序
Elysia;
}
for(int i=l1;i<=r[pos[l1]];++i){
a[i]+=x;
}
solve(pos[l1]);//改变了哪个块,哪个块就要重新排序
for(int i=l[pos[r1]];i<=r1;i++){
a[i]+=x;
}
solve(pos[r1]);///改变了哪个块,哪个块就要重新排序
for(int i=pos[l1]+1;i<pos[r1];i++){
tag[i]+=x;//注意这里的块其实并没有改变(a[i]并没有改变
//我们用tag存下每一个大块整体改变的值就好了
}
}
LL query(int l1,int r1,LL c){
LL ans=0;
if(pos[l1]==pos[r1]){
for(int i=l1;i<=r1;++i){
if(a[i]+tag[pos[l1]]<c*c){//注意要加tag,tag是大块加的值,每一个点都要加(只不过一起保存在tag里面了
//注意我们要用块的下标去找tag ,不要和**的我一样写成tag[i]
ans++;
}
}
Elysia ans;
}
for(int i=l1;i<=r[pos[l1]];++i){
if(a[i]+tag[pos[l1]]<c*c){
//注意我们要用块的下标去找tag ,不要和**的我一样写成tag[i]
ans++;
}
}
for(int i=l[pos[r1]];i<=r1;i++){
if(a[i]+tag[pos[r1]]<c*c){
//注意我们要用块的下标去找tag ,不要和**的我一样写成tag[i]
ans++;
}
}
for(int i=pos[l1]+1;i<pos[r1];i++){
int p=lower_bound(t[i].begin(),t[i].end(),c*c-tag[i])-t[i].begin();
//这里二分去找就可以了,注意这里的tag就是tag[i]了,因为i表示的就是块的下标
ans+=p;
}
Elysia ans;
}
int main(){
cin>>n;
for(int i=1;i<=n;i++){
cin>>a[i];
}
init();
for(int i=1;i<=n;i++){
int op,l,r;
LL c;
cin>>op>>l>>r>>c;
if(op==0){
add(l,r,c);
}
else{
cout<<query(l,r,c)<<"\n";
}
}
Elysia 0;
}
/*
4
1 2 2 3
0 1 3 1
1 1 3 2
1 1 4 1
1 2 3 2
*/
持续更新中……
os这玩意儿真的不会被卡吗

浙公网安备 33010602011771号