LOJ 数列分块入门 9 题解题报告

\(\text{By DaiRuiChen007}\)

I. 数列分块入门 1

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持区间加,单点查值

思路分析

简单分块,块长 \(\sqrt n\),对于每个块维护一个懒标记

代码呈现

#include<bits/stdc++.h>
using namespace std;
const int MAXN=5e4+1,SQR=231;
int a[MAXN],bel[MAXN];
int tag[SQR],lp[SQR],rp[SQR];
inline void Modify(int l,int r,int c) {
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) a[i]+=c;
return ;
}
for(int i=l;i<=rp[bel[l]];++i) a[i]+=c;
for(int i=lp[bel[r]];i<=r;++i) a[i]+=c;
for(int i=bel[l]+1;i<=bel[r]-1;++i) tag[i]+=c;
return ;
}
signed main() {
int n,k;
scanf("%d",&n);
k=sqrt(n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=k;++i) lp[i]=(i-1)*k+1,rp[i]=i*k;
rp[k]=n;
for(int i=1;i<=k;++i) for(int j=lp[i];j<=rp[i];++j) bel[j]=i;
while(n--) {
int opt,l,r,c;
scanf("%d%d%d%d",&opt,&l,&r,&c);
if(opt==0) Modify(l,r,c);
if(opt==1) printf("%d\n",a[r]+tag[bel[r]]);
}
return 0;
}

II. 数列分块入门2

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持区间加,区间统计小于 \(k\) 的数的个数

思路分析

区间加,传统思路,维护懒标记

区间计数,对于每个序列维护一个有序集合(排序后的 vector 或直接使用 set)都可以

然后对于每一个块在有序集上二分小于 \(k-tag\) 的个数

分块的重要注意事项:如果在修改了零散块的时候会影响区间标记的正确性,需要先下放区间标记再修改零散块

在本题中,如果修改零散块时,要把对应块维护的有序集清空然后重新加入维护

代码呈现

#include<bits/stdc++.h>
using namespace std;
const int MAXN=50001,SQR=231;
int a[MAXN],bel[MAXN];
int tag[SQR],lp[SQR],rp[SQR];
vector <int> f[SQR];
inline void Update(int id) {
f[id].clear();
for(int i=lp[id];i<=rp[id];++i) f[id].push_back(a[i]);
sort(f[id].begin(),f[id].end());
return ;
}
inline void Modify(int l,int r,int v) {
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) a[i]=a[i]+v;
Update(bel[l]);
return ;
}
for(int i=l;i<=rp[bel[l]];++i) a[i]=a[i]+v;
Update(bel[l]);
for(int i=lp[bel[r]];i<=r;++i) a[i]=a[i]+v;
Update(bel[r]);
for(int i=bel[l]+1;i<=bel[r]-1;++i) tag[i]+=v;
}
inline int Query(int l,int r,int v) {
int ans=0;
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) if(a[i]+tag[bel[i]]<v) ++ans;
return ans;
}
for(int i=l;i<=rp[bel[l]];++i) if(a[i]+tag[bel[i]]<v) ++ans;
for(int i=lp[bel[r]];i<=r;++i) if(a[i]+tag[bel[i]]<v) ++ans;
for(int i=bel[l]+1;i<=bel[r]-1;++i) ans+=lower_bound(f[i].begin(),f[i].end(),v-tag[i])-f[i].begin();
return ans;
}
signed main() {
int n,k;
scanf("%d",&n);
k=sqrt(n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=k;++i) lp[i]=(i-1)*k+1,rp[i]=i*k;
rp[k]=n;
for(int i=1;i<=k;++i) {
for(int j=lp[i];j<=rp[i];++j) bel[j]=i;
Update(i);
}
while(n--) {
int opt,l,r,c;
scanf("%d%d%d%d",&opt,&l,&r,&c);
if(opt==0) Modify(l,r,c);
if(opt==1) printf("%d\n",Query(l,r,c*c));
}
return 0;
}

III. 数列分块入门 3

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持区间加,区间查询前驱

思路分析

和上一题几乎一样,直接有序集上二分即可

代码呈现

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+1,SQR=321,INF=2147483647;
int a[MAXN],bel[MAXN];
int tag[SQR],lp[SQR],rp[SQR];
vector <int> f[SQR];
inline void Update(int id) {
f[id].clear();
for(int i=lp[id];i<=rp[id];++i) f[id].push_back(a[i]);
sort(f[id].begin(),f[id].end());
return ;
}
inline void Modify(int l,int r,int v) {
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) a[i]+=v;
Update(bel[l]);
return ;
}
for(int i=l;i<=rp[bel[l]];++i) a[i]+=v;
Update(bel[l]);
for(int i=lp[bel[r]];i<=r;++i) a[i]+=v;
Update(bel[r]);
for(int i=bel[l]+1;i<=bel[r]-1;++i) tag[i]+=v;
return ;
}
inline int query(int l,int r,int v) {
int ans=-INF,ok=false;
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) if(a[i]+tag[bel[i]]<v) ok=true,ans=max(ans,a[i]+tag[bel[i]]);
if(!ok) return -1;
else return ans;
}
for(int i=l;i<=rp[bel[l]];++i) if(a[i]+tag[bel[i]]<v) ok=true,ans=max(ans,a[i]+tag[bel[i]]);
for(int i=lp[bel[r]];i<=r;++i) if(a[i]+tag[bel[i]]<v) ok=true,ans=max(ans,a[i]+tag[bel[i]]);
for(int i=bel[l]+1;i<=bel[r]-1;++i) {
auto it=lower_bound(f[i].begin(),f[i].end(),v-tag[i]);
if(it!=f[i].begin()) ok=true,ans=max(ans,*(--it)+tag[i]);
}
if(!ok) return -1;
else return ans;
}
signed main() {
int n,k;
scanf("%d",&n);
k=sqrt(n);
for(int i=1;i<=n;++i) scanf("%d",&a[i]);
for(int i=1;i<=k;++i) lp[i]=(i-1)*k+1,rp[i]=i*k;
rp[k]=n;
for(int i=1;i<=k;++i) {
for(int j=lp[i];j<=rp[i];++j) bel[j]=i;
Update(i);
}
while(n--) {
int opt,l,r,c;
scanf("%d%d%d%d",&opt,&l,&r,&c);
if(opt==0) Modify(l,r,c);
if(opt==1) printf("%d\n",query(l,r,c));
}
return 0;
}

IV. 数列分块入门 4

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的数,支持区间加,查询区间和

思路分析

查询区间和只需要在查询单点和的基础上维护每一个整块的和

类似线段树,在打懒标记的时候同时更新一下区间和

代码呈现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=50001,SQR=231;
int a[MAXN],bel[MAXN];
int tag[SQR],lp[SQR],rp[SQR],sum[SQR];
inline void Modify(int l,int r,int v) {
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) sum[bel[i]]+=v,a[i]+=v;
return ;
}
for(int i=l;i<=rp[bel[l]];++i) sum[bel[i]]+=v,a[i]+=v;
for(int i=lp[bel[r]];i<=r;++i) sum[bel[i]]+=v,a[i]+=v;
for(int i=bel[l]+1;i<=bel[r]-1;++i) tag[i]+=v;
}
inline int Query(int l,int r) {
int ans=0;
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) ans+=a[i]+tag[bel[i]];
return ans;
}
for(int i=l;i<=rp[bel[l]];++i) ans+=a[i]+tag[bel[i]];
for(int i=lp[bel[r]];i<=r;++i) ans+=a[i]+tag[bel[i]];
for(int i=bel[l]+1;i<=bel[r]-1;++i) ans+=sum[i]+tag[i]*(rp[i]-lp[i]+1);
return ans;
}
signed main() {
int n,k;
scanf("%lld",&n);
k=sqrt(n);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
for(int i=1;i<=k;++i) lp[i]=(i-1)*k+1,rp[i]=i*k;
rp[k]=n;
for(int i=1;i<=k;++i) {
for(int j=lp[i];j<=rp[i];++j) sum[i]+=a[j],bel[j]=i;
}
while(n--) {
int opt,l,r,c;
scanf("%lld%lld%lld%lld",&opt,&l,&r,&c);
if(opt==0) Modify(l,r,c);
if(opt==1) printf("%lld\n",Query(l,r)%(c+1));
}
return 0;
}

V. 数列分块入门 5

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持区间开根,查询区间和

思路分析

注意到数据范围里的每个数开根次数不超过 \(6\) 次就会变成 \(0\) 或 \(1\),然后无论开根多少次值都不会变

所以对于每一个块,维护当前块中大于 \(1\) 的数的个数, 如果一个块内没有大于 \(1\) 的数,那么修改的时候直接跳过这个块,否则暴力对于每一个位置修改(其实可以用记录下一个大于 \(1\) 的数,但是没啥用)

然后维护一个区间和,经典求区间和模板即可

代码呈现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=5e4+1,SQR=231;
int a[MAXN],bel[MAXN];
int lp[SQR],rp[SQR],sum[SQR],siz[SQR];
inline void Modify(int l,int r) {
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) {
if(a[i]<=1) continue;
sum[bel[i]]-=a[i];
a[i]=sqrt(a[i]);
sum[bel[i]]+=a[i];
if(a[i]<=1) --siz[bel[i]];
}
return ;
}
for(int i=l;i<=rp[bel[l]];++i) {
if(a[i]<=1) continue;
sum[bel[i]]-=a[i];
a[i]=sqrt(a[i]);
sum[bel[i]]+=a[i];
if(a[i]<=1) --siz[bel[i]];
}
for(int i=lp[bel[r]];i<=r;++i) {
if(a[i]<=1) continue;
sum[bel[i]]-=a[i];
a[i]=sqrt(a[i]);
sum[bel[i]]+=a[i];
if(a[i]<=1) --siz[bel[i]];
}
for(int i=bel[l]+1;i<=bel[r]-1;++i) {
if(!siz[i]) continue;
for(int j=lp[i];j<=rp[i];++j) {
if(a[j]<=1) continue;
sum[i]-=a[j];
a[j]=sqrt(a[j]);
sum[i]+=a[j];
if(a[j]<=1) --siz[i];
}
}
return ;
}
inline int Query(int l,int r) {
int res=0;
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) res+=a[i];
return res;
}
for(int i=l;i<=rp[bel[l]];++i) res+=a[i];
for(int i=lp[bel[r]];i<=r;++i) res+=a[i];
for(int i=bel[l]+1;i<=bel[r]-1;++i) res+=sum[i];
return res;
}
signed main() {
int n;
scanf("%lld",&n);
for(int i=1;i<=n;++i) scanf("%lld",&a[i]);
int k=sqrt(n);
for(int i=1;i<=k;++i) {
lp[i]=k*(i-1)+1;
rp[i]=i==k?n:k*i;
for(int j=lp[i];j<=rp[i];++j) {
bel[j]=i,sum[i]+=a[j];
if(a[j]>1) ++siz[i];
}
}
for(int i=1;i<=n;++i) {
int opt,l,r,c;
scanf("%lld%lld%lld%lld",&opt,&l,&r,&c);
if(opt==0) Modify(l,r);
if(opt==1) printf("%lld\n",Query(l,r));
}
return 0;
}

VI. 数列分块入门 6

题目大意

\(\text{Link}\)

维护一个不定长序列,支持单点插入,单点查值

思路分析

对于每个块,用一个 vector 维护其中的每一个数

对于查询操作,先从第一块开始,整块整块地跳,然后找到所在的块的对应位置然后输出

对于插入操作,同理找到对应的块中对应的位置,暴力 insert 即可

为了保证分块的时间复杂度,需要对整个序列进行定期重构,也就是将原本的序列重新分块

如果某一个块的大小大于初始块长的特定倍数(建议 \(12\sim 20\) 等倍数均可,视个人喜好而定),就可以对整个序列重新分块

代码呈现

#include<bits/stdc++.h>
using namespace std;
const int SQR=451;
vector <int> seq[SQR];
int n,k;
inline int bel(int x) { return (x+k-1)/k; }
inline void Rebuild() {
vector <int> vec;
for(int i=1;i<=k;++i) {
for(int v:seq[i]) vec.push_back(v);
seq[i].clear();
}
n=vec.size(),k=sqrt(n);
for(int i=1;i<=n;++i) seq[bel(i)].push_back(vec[i-1]);
k=bel(n);
return ;
}
inline int Query(int p) {
int block=1;
while(p>seq[block].size()) {
p-=seq[block].size();
++block;
}
return seq[block][p-1];
}
inline void Insert(int p,int v) {
int block=1;
while(p>seq[block].size()) {
p-=seq[block].size();
++block;
}
seq[block].insert(seq[block].begin()+p-1,v);
if(seq[block].size()>(k<<4)) Rebuild();
return ;
}
signed main() {
scanf("%d",&n);
int q=n;k=sqrt(n);
for(int i=1;i<=n;++i) {
int x;
scanf("%d",&x);
seq[bel(i)].push_back(x);
}
k=bel(n);
while(q--) {
int opt,l,r,c;
scanf("%d%d%d%d",&opt,&l,&r,&c);
if(opt==0) Insert(l,r);
if(opt==1) printf("%d\n",Query(r));
}
return 0;
}

VII. 数列分块入门 7

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持区间加,区间乘,查询单点值

思路分析

线段树2的分块版本,同理维护乘法标记和加法标记,注意修改零散块之前要先下放标记,并且计算标记贡献的时候是先乘后加的

具体的实现细节可以先写一下线段树2然后再来写,思路会清晰得多

代码呈现

#include<bits/stdc++.h>
#define int long long
using namespace std;
const int MAXN=1e5+1,SQR=321,MOD=10007;
int add[SQR],mul[SQR],lp[SQR],rp[SQR],sum[SQR];
int a[MAXN],bel[MAXN];
inline void PushDown(int id) {
for(int i=lp[id];i<=rp[id];++i) a[i]=(a[i]*mul[id]%MOD+add[id])%MOD;
mul[id]=1,add[id]=0;
return ;
}
inline void ModifyAdd(int l,int r,int v) {
if(bel[l]==bel[r]) {
PushDown(bel[l]);
for(int i=l;i<=r;++i) a[i]=(a[i]+v)%MOD;
return ;
}
PushDown(bel[l]);
for(int i=l;i<=rp[bel[l]];++i) a[i]=(a[i]+v)%MOD;
PushDown(bel[r]);
for(int i=lp[bel[r]];i<=r;++i) a[i]=(a[i]+v)%MOD;
for(int i=bel[l]+1;i<=bel[r]-1;++i) add[i]=(add[i]+v)%MOD;
return ;
}
inline void ModifyMul(int l,int r,int v) {
if(bel[l]==bel[r]) {
PushDown(bel[l]);
for(int i=l;i<=r;++i) a[i]=a[i]*v%MOD;
return ;
}
PushDown(bel[l]);
for(int i=l;i<=rp[bel[l]];++i) a[i]=a[i]*v%MOD;
PushDown(bel[r]);
for(int i=lp[bel[r]];i<=r;++i) a[i]=a[i]*v%MOD;
for(int i=bel[l]+1;i<=bel[r]-1;++i) mul[i]=mul[i]*v%MOD,add[i]=add[i]*v%MOD;
return ;
}
inline int Query(int p) {
return (a[p]*mul[bel[p]]+add[bel[p]])%MOD;
}
signed main() {
int n,k;
scanf("%lld",&n);
k=sqrt(n);
for(int i=1;i<=k;++i) {
lp[i]=k*(i-1)+1;
rp[i]=i==k?n:k*i;
add[i]=0,mul[i]=1;
for(int j=lp[i];j<=rp[i];++j) scanf("%lld",&a[j]),bel[j]=i;
}
for(int i=1;i<=n;++i) {
int opt,l,r,c;
scanf("%lld%lld%lld%lld",&opt,&l,&r,&c);
if(opt==0) ModifyAdd(l,r,c);
if(opt==1) ModifyMul(l,r,c);
if(opt==2) printf("%lld\n",Query(r));
}
return 0;
}

VIII. 数列分块入门 8

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持查询区间值等于 \(c\) 的元素个数,并且将整个区间元素推平成 \(c\)

思路分析

在简单暴力的基础上分一下块,考虑一个显而易见的优化:维护每个块内是否全部等于某个数,如果是则直接修改并跳过,否则暴力对于每一个元素修改并更行区间标记,对于零散块则下放标记并且暴力修改

这样做的单次均摊复杂度是 \(\Theta(\sqrt n)\) 的,具体的证明援引自 命题人 hzwer 大佬的题解

这样看似最差情况每次都会耗费 \(\Theta(n)\) 的时间,但其实可以这样分析:

假设初始序列都是同一个值,那么查询是 \(\Theta(\sqrt n)\),如果这时进行一个区间操作,它最多破坏首尾 \(2\) 个块的标记,所以只能使后面的询问至多多 \(2\) 个块的暴力时间,所以均摊每次操作复杂度还是 \(\Theta(\sqrt n)\)

换句话说,要想让一个操作耗费 \(\Theta(n)\)的时间,要先花费 \(\Theta(\sqrt n)\) 个操作对数列进行修改

初始序列不同值,经过类似分析后,就可以放心的暴力啦

——hzwer(本系列题目命题人)

Orz。。。

代码呈现

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+1,SQR=321;
int a[MAXN],bel[MAXN],lp[SQR],rp[SQR],tag[SQR];
inline void PushDown(int id) {
if(tag[id]==-1) return ;
for(int i=lp[id];i<=rp[id];++i) a[i]=tag[id];
tag[id]=-1;
return ;
}
inline int Solve(int l,int r,int v) {
int res=0;
if(bel[l]==bel[r]) {
PushDown(bel[l]);
for(int i=l;i<=r;++i) {
if(a[i]==v) ++res;
else a[i]=v;
}
return res;
}
PushDown(bel[l]);
for(int i=l;i<=rp[bel[l]];++i) {
if(a[i]==v) ++res;
else a[i]=v;
}
PushDown(bel[r]);
for(int i=lp[bel[r]];i<=r;++i) {
if(a[i]==v) ++res;
else a[i]=v;
}
for(int i=bel[l]+1;i<=bel[r]-1;++i) {
if(tag[i]==-1) {
for(int j=lp[i];j<=rp[i];++j) {
if(a[j]==v) ++res;
else a[j]=v;
}
tag[i]=v;
} else {
if(tag[i]==v) res+=rp[i]-lp[i]+1;
else tag[i]=v;
}
}
return res;
}
signed main() {
int n,k;
scanf("%d",&n);
k=sqrt(n);
for(int i=1;i<=k;++i) {
lp[i]=(i-1)*k+1;
rp[i]=i==k?n:i*k;
tag[i]=-1;
for(int j=lp[i];j<=rp[i];++j) scanf("%d",&a[j]);
}
for(int i=1;i<=n;++i) {
int l,r,c;
scanf("%d%d%d",&l,&r,&c);
printf("%d\n",Solve(l,r,c));
}
return 0;
}

IX. 数列分块入门 9

题目大意

\(\text{Link}\)

维护一个长度为 \(n\) 的序列,支持查询值最小的区间众数

思路分析

不管怎么样看到标题就先把块分了再说

先分块,对于一个查询区间,分成整块+零散块

考虑可能成为答案的数:零散块中的都有可能,整块中只有可能是这一段的众数。如果不是整块的众数的话,那么一定在零散块中出现过(自行感性理解)

所以只需要离散化,然后将每个数的出现位置存下来,可以做到 \(\Theta(\log n)\) 查询某个数在区间里的出现次数

同时,需要预处理出任意两个块之间的区间众数,方便查询

然后对于每次查询,只需要对于整块里的众数和零散块里的数都枚举一遍,计算出现次数,取众数最小值即可

复杂度 \(\Theta(n\sqrt n\log n)\)

代码呈现

#include<bits/stdc++.h>
using namespace std;
const int MAXN=1e5+1,SQR=321;
int mdl[SQR][SQR],cnt[MAXN],a[MAXN],bel[MAXN],lp[SQR],rp[SQR];
vector <int> pos[MAXN],dsc;
inline int count(int x,int l,int r) {
return upper_bound(pos[x].begin(),pos[x].end(),r)-lower_bound(pos[x].begin(),pos[x].end(),l);
}
inline int Query(int l,int r) {
int res=0,tot=0;
if(bel[l]==bel[r]) {
for(int i=l;i<=r;++i) {
int tmp=count(a[i],l,r);
if(tmp>tot||(a[i]<res&&tmp==tot)) tot=tmp,res=a[i];
}
return dsc[res-1];
}
if(bel[r]-bel[l]>1) res=mdl[bel[l]+1][bel[r]-1],tot=count(res,l,r);
for(int i=l;i<=rp[bel[l]];++i) {
int tmp=count(a[i],l,r);
if(tmp>tot||(a[i]<res&&tmp==tot)) tot=tmp,res=a[i];
}
for(int i=lp[bel[r]];i<=r;++i) {
int tmp=count(a[i],l,r);
if(tmp>tot||(a[i]<res&&tmp==tot)) tot=tmp,res=a[i];
}
return dsc[res-1];
}
signed main() {
int n;
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%d",&a[i]);
dsc.push_back(a[i]);
}
sort(dsc.begin(),dsc.end());
dsc.erase(unique(dsc.begin(),dsc.end()),dsc.end());
int k=sqrt(n),m=(n+k-1)/k;
for(int i=1;i<=m;++i) {
lp[i]=(i-1)*k+1;
rp[i]=i==m?n:i*k;
for(int j=lp[i];j<=rp[i];++j) {
a[j]=(lower_bound(dsc.begin(),dsc.end(),a[j])-dsc.begin())+1;
bel[j]=i;
pos[a[j]].push_back(j);
}
}
for(int i=1;i<=m;++i) {
for(int i=1;i<=n;++i) cnt[i]=0;
int res=0,tot=0;
for(int j=lp[i];j<=n;++j) {
++cnt[a[j]];
if(cnt[a[j]]>tot||(cnt[a[j]]==tot&&a[j]<res)) {
tot=cnt[a[j]];
res=a[j];
}
mdl[i][bel[j]]=res;
}
}
for(int i=1;i<=n;++i) {
int l,r;
scanf("%d%d",&l,&r);
printf("%d\n",Query(l,r));
}
return 0;
}

LOJ 数列分块入门 9 题解题报告的更多相关文章

  1. [Loj] 数列分块入门 1 - 9

    数列分块入门 1 https://loj.ac/problem/6277 区间加 + 单点查询 #include <iostream> #include <cstdio> #i ...

  2. 数列分块入门九题(三):LOJ6283~6285

    Preface 最后一题我一直觉得用莫队是最好的. 数列分块入门 7--区间乘法,区间加法,单点询问 还是很简单的吧,比起数列分块入门 7就多了个区间乘. 类似于线段树,由于乘法的优先级高于加法,因此 ...

  3. 数列分块入门九题(二):LOJ6280~6282

    Preface 个人感觉这中间的三题是最水的没有之一 数列分块入门 4--区间加法,区间求和 这个也是很多数据结构完爆的题目线段树入门题,但是练分块我们就要写吗 修改还是与之前类似,只不过我们要维护每 ...

  4. 数列分块入门九题(一):LOJ6277~6279

    Preface 分块,一个神奇的暴力算法.可以把很多\(O(n^2)\)的数据结构题的暴力优化到常数极小的\(O(n\sqrt n)\).当一些毒瘤题无法用线段树,主席树,平衡树,树状数组...... ...

  5. loj 数列分块入门 5 7 8

    5 题意 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及区间开方,区间求和. 思路 用\(tag\)记录这一块是否已全为\(1\). 除分块外,还可用 树状数组+并查集(链表) 或者 线 ...

  6. loj 数列分块入门 6 9(区间众数)

    6 题意 给出一个长为\(n\)的数列,以及\(n\)个操作,操作涉及单点插入,单点询问,数据随机生成. 题解 参考:http://hzwer.com/8053.html 每个块内用一个\(vecto ...

  7. LOJ 数列分块入门系列

    目录 1.区间加+单点查 每个块维护tag,散的暴力改. code: #include<bits/stdc++.h> using namespace std; const int maxn ...

  8. LOJ6285 数列分块入门9(分块)

    昨天对着代码看了一晚上 然后今天终于在loj上过了 数列分块入门9题撒花★,°:.☆( ̄▽ ̄)/$:.°★ . 然后相当玄学 块的大小调成\(\sqrt{n}\)会TLE,改成150就过了 啧 然后就 ...

  9. LOJ——#6277. 数列分块入门 1

    ~~推荐播客~~ 「分块」数列分块入门1 – 9 by hzwer 浅谈基础根号算法——分块 博主蒟蒻,有缘人可直接观摩以上大佬的博客... #6277. 数列分块入门 1 题目大意: 给出一个长为 ...

随机推荐

  1. JS复制粘贴效果

    话不多说.直接上代码 HTML: 1 <div> 2 老师入会密码:<input type="text" id="tPass"> < ...

  2. 漫谈Entity-Component-System

    原文链接 简介 对于很多人来说,ECS只是一个可以提升性能的架构,但是我觉得ECS更强大的地方在于可以降低代码复杂度. 在游戏项目开发的过程中,一般会使用OOP的设计方式让GameObject处理自身 ...

  3. 并发编程之 ThreadLocal

    前言 了解过 SimpleDateFormat 时间工具类的朋友都知道,该工具类非常好用,可以利用该类可以将日期转换成文本,或者将文本转换成日期,时间戳同样也可以. 以下代码,我们采用通用的 Simp ...

  4. Abp.Zero 手机号免密登录验证与号码绑定功能的实现(一):验证码模块

    这是一篇系列博文,我将使用Abp.Zero搭建一套集成手机号免密登录验证与号码绑定功能的用户系统: Abp.Zero 手机号免密登录验证与号码绑定功能的实现(一):验证码模块 Abp.Zero 手机号 ...

  5. jmeter接口自动化-通过csv文件读取用例并执行测试

    最近在公司测试中经常使用jmeter这个工具进行接口自动化,简单记录下~ 一.在csv文件中编写好用例 首先在csv文件首行填写相关参数(可根据具体情况而定)并编写测试用例.脚本可通过优先级参数控制执 ...

  6. WinDBG详解进程初始化dll是如何加载的

    一:背景 1.讲故事 有朋友咨询个问题,他每次在调试 WinDbg 的时候,进程初始化断点之前都会有一些 dll 加载到进程中,比如下面这样: Microsoft (R) Windows Debugg ...

  7. spring源码解析(二) 结合源码聊聊FactoryBean

    一.什么是FactoryBean FactoryBean是由spring提供的用来让用户可以自定bean创建的接口:实现该接口可以让你的bean不用经过spring复杂的bean创建过程,但同时也能做 ...

  8. 如何使用webgl(three.js)实现3D储能,3D储能站,3D智慧储能、储能柜的三维可视化解决方案——第十七课

    前言 上节课我们讲了<3D光伏发电>,与之配套的就是能量存储 这节课我们主要讲讲储能,储能站,在分布式能源系统中起到调节用对电的尖峰平谷进行削峰填谷的作用.特别是小型储能站,更加灵活,因地 ...

  9. 关于软件物料清单(SBOM),你所需要了解的一切

    在此前的多篇文章中,我们已经详细地介绍了软件物料清单(SBOM)对于保障软件供应链安全的重要性以及一些注意事项.在本文中,我们将会更深入地介绍SBOM,包括最低要求元素.格式.使用场景以及如何对其进行 ...

  10. jquery实现复选框的全选与取消全选功能

    HTML代码 首先创建一个表格: <table class="table table-bordered table-hover"> <tr> <th& ...