数列分块入笔记
前言
这篇文章不是正经的分块,仅记录做题记录
数列分块 1
我表示这不是线段树吗
#include<bits/stdc++.h>
#define int long long
#define endl '\n'
#define ls id<<1
#define rs id<<1|1
using namespace std;
const int maxn=500000+10;
struct Node{
int L,R;
int sum,lazy,ma;
} t[maxn*4];
int n,a[maxn];
void up(int id){
t[id].sum=t[ls].sum+t[rs].sum;
t[id].ma=max(t[ls].ma,t[rs].ma);
}
void down(int id){//下传
t[ls].lazy+=t[id].lazy; t[rs].lazy+=t[id].lazy;
t[ls].sum+=(t[ls].R-t[ls].L+1)*t[id].lazy;
t[ls].ma+=t[id].lazy;
t[rs].sum+=(t[rs].R-t[rs].L+1)*t[id].lazy;
t[rs].ma+=t[id].lazy;
t[id].lazy=0;
}
void build(int id,int l,int r){//建立
t[id].L=l; t[id].R=r;
if(l==r){
t[id].sum=a[l]; t[id].ma=a[l];
return;
}
int mid=(l+r)/2;
build(ls,l,mid); //左子树
build(rs,mid+1,r);//右子树
up(id);
}
void update(int id,int l,int r,int val){//将l,r区间同时加上一个val
if(t[id].L>r||t[id].R<l) return;
if(t[id].L>=l&&t[id].R<=r){//节点自己处理
t[id].lazy+=val;
t[id].sum+=(t[id].R-t[id].L+1)*val;
t[id].ma+=val;
return;
}
if(t[id].lazy!=0) down(id);
update(ls,l,r,val); update(rs,l,r,val);
up(id);
}//修改操作
int ask_sum(int id,int l,int r){//查询元素的和
if(t[id].L>r||t[id].R<l) return 0;
if(t[id].L>=l&&t[id].R<=r) return t[id].sum;
if(t[id].lazy !=0) down(id);
return ask_sum(ls,l,r)+ask_sum(rs,l,r);
}
signed main(){
int m,opt,l,r,x,val;
cin>>n;
for(int i=1;i<=n;i++) cin>>a[i];
build(1,1,n);
while(n--){
cin>>opt>>l>>r>>val;
if(opt==0){
update(1,l,r,val);
}
else if(opt==1){
cout<<ask_sum(1,r,r)<<endl;
}
}
return 0;
}
数列分块 2
完了。线段树好像做不了了。
那只能老实写分块,维护区间有序即可。
区间修改很简单,关键就是要维护有序块才能查询。
#include<bits/stdc++.h>
using namespace std;
#define int long long
int n,T;
const int maxn=2e5+10,sq=450;
int a[maxn],lazy[sq],b[maxn];
int pos(int x){return (x-1)/T+1;}//block id
int L(int bid){return (bid-1)*T+1;}
int R(int bid){return min(bid*T,n);}
void sb(int bid){
for(int i=L(bid);i<=R(bid);i++)b[i]=a[i];
sort(b+L(bid),b+R(bid)+1);
}
void update(int l,int r,int val){
int pl=pos(l),pr=pos(r);
if(pl==pr){
for(int i=l;i<=r;i++){
a[i]+=val;
}
sb(pl);
return;
}
for(int i=l;i<=R(pl);i++)a[i]+=val;
sb(pl);
for(int i=L(pr);i<=r;i++)a[i]+=val;
sb(pr);
for(int i=pl+1;i<pr;i++)lazy[i]+=val;
}
int query(int l,int r,int c){
int pl=pos(l),pr=pos(r),ans=0;
if(pl==pr){
for(int i=l;i<=r;i++)if(a[i]+lazy[pl]<c*c)ans++;
return ans;
}
for(int i=l;i<=R(pl);i++)if(a[i]+lazy[pl]<c*c)ans++;
for(int i=L(pr);i<=r;i++)if(a[i]+lazy[pr]<c*c)ans++;
for(int i=pl+1;i<pr;i++){
ans+=lower_bound(b+L(i),b+R(i)+1,c*c-lazy[i])-b-L(i);//统计a[i]<c^2-lazy[bid]的个数
}
return ans;
}
signed main(){
cin>>n;
T=ceil(sqrt(n));
for(int i=1;i<=n;i++)cin>>a[i];
for(int i=1;i<=pos(n);i++){
sb(i);
}
for(int i=1;i<=n;i++){
int opt;
cin>>opt;
if(opt==0){
int l,r,c;
cin>>l>>r>>c;
update(l,r,c);
}
else{
int l,r,c;
cin>>l>>r>>c;
cout<<query(l,r,c)<<endl;
}
}
return 0;
}
数列分块 3
未做
数列分块 4
不说了,线段树,启动!
初始模数取大点。\(100000000000000\)
#include<bits/stdc++.h>
#define int long long
#define ls id<<1
#define rs id<<1|1
int P;
#define endl '\n'
using namespace std;
const int maxn=400000+10;
struct Node{
int L,R;
int sum,add,mul;//mul:乘法
} t[maxn*4+1];
int n,a[maxn];
void up(int id){
t[id].sum=t[ls].sum+t[rs].sum;
}
void down(int id){//处理编号id的为标记,下传给它的儿子
t[ls].add=(t[ls].add*t[id].mul+t[id].add)%P;
t[rs].add=(t[rs].add*t[id].mul+t[id].add)%P;
t[ls].sum=(t[ls].sum*t[id].mul+(t[ls].R-t[ls].L+1)*t[id].add%P)%P;
t[rs].sum=(t[rs].sum*t[id].mul+(t[rs].R-t[rs].L+1)*t[id].add%P)%P;
t[ls].mul*=t[id].mul;t[ls].mul%=P;
t[rs].mul*=t[id].mul;t[rs].mul%=P;
t[id].add=0;t[id].mul=1;
}
void build(int id,int l,int r){//新建一个节点,编号为id,左端点为l,右端点为r
t[id].L=l;t[id].R=r;
if(l==r){//节点表示的区间为一个点,递归边界
t[id].sum=a[l];
return;
}
t[id].add=0,t[id].mul=1;
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
t[id].sum=(t[ls].sum+t[rs].sum);t[id].sum%=P;
}
void update(int id,int l,int r,int val){//将l,r区间同时加上一个val
if(t[id].L>r||t[id].R<l) return;
if(t[id].L>=l&&t[id].R<=r){
t[id].add+=val;t[id].add%=P;
t[id].sum+=(t[id].R-t[id].L+1)*val;t[id].sum%=P;
return;
}
if(t[id].add!=0||t[id].mul!=1)down(id);
update(ls,l,r,val);update(rs,l,r,val);
t[id].sum=(t[ls].sum+t[rs].sum);t[id].sum%=P;
return;
}
int ask_sum(int id,int l,int r){
if(t[id].L>r||t[id].R<l) return 0;
if(t[id].L>=l&&t[id].R<=r) return t[id].sum%P;
if(t[id].add!=0||t[id].mul!=1)down(id);
return (ask_sum(ls,l,r)+ask_sum(rs,l,r))%P;
}
signed main(){
int m,opt,l,r,val;
cin>>n;
P=100000000000000;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(n--){
cin>>opt>>l>>r>>val;
if(opt==0){
update(1,l,r,val);
}
else {
cout<<(ask_sum(1,l,r)%(val+1)+val+1)%(val+1)<<endl;
}
}
return 0;
}
数列分块 5
势能线段树,如果要区间开根的话,判断区间最大值是否为 \(1\),如果是 \(1\) 就直接不递归了。
从 花神游历各国 过来的。
#include<bits/stdc++.h>
using namespace std;
#define int long long
#define ls id<<1
#define rs id<<1|1
const int maxn=400000+10;
struct Node{
int l,r,add,sum,ma;
}t[maxn*4];
int a[maxn];
void up(int id){
t[id].sum=t[ls].sum+t[rs].sum;
t[id].ma=max(t[ls].ma,t[rs].ma);
}
void down(int id){
t[ls].add+=t[id].add;
t[rs].add+=t[id].add;
t[ls].sum+=(t[ls].r-t[ls].l+1)*t[id].add;
t[rs].sum+=(t[rs].r-t[rs].l+1)*t[id].add;
t[ls].ma+=t[id].add;
t[rs].ma+=t[id].add;
t[id].add=0;
}
void build(int id,int l,int r){
t[id].l=l,t[id].r=r;
if(l==r){
t[id].sum=a[l];
t[id].ma=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid);
build(rs,mid+1,r);
up(id);
}
void update(int id,int x,int val){//单点修改
if(t[id].l>x||t[id].r<x)return;
if(t[id].l>=x&&t[id].r<=x){
t[id].add+=val;
t[id].sum+=(t[id].r-t[id].l+1)*val;
t[id].ma+=val;
return;
}
if(t[id].add)down(id);//下放优化
update(ls,x,val);
update(rs,x,val);
up(id);
}
int ask_sum(int id,int l,int r){
if(t[id].l>r||t[id].r<l)return 0;
if(t[id].l>=l&&t[id].r<=r){
return t[id].sum;
}
if(t[id].add)down(id);
return ask_sum(ls,l,r)+ask_sum(rs,l,r);
}
int ask_max(int id,int l,int r){
if(t[id].l>r||t[id].r<l)return -0x7f7f7f7f;
if(t[id].l>=l&&t[id].r<=r){
return t[id].ma;
}
if(t[id].add)down(id);
return max(ask_max(ls,l,r),ask_max(rs,l,r));
}
void upsqrt(int x){//单点开方
int dif=ask_sum(1,x,x)-(int)floor(sqrtl(ask_sum(1,x,x)));
update(1,x,-dif);
}
void dosqrt(int id,int l,int r){
if(t[id].l>r||t[id].r<l)return;
if(t[id].l==t[id].r){
upsqrt(t[id].l);
return;
}
if(t[ls].ma>1)dosqrt(ls,l,r);
if(t[rs].ma>1)dosqrt(rs,l,r);
up(id);
}
signed main(){
int n;
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(n--){
int opt,l,r;
cin>>opt;
if(opt==0){
cin>>l>>r;
if(l>r)swap(l,r);
if(ask_max(1,l,r)<=1)continue;
dosqrt(1,l,r);
}
else{
cin>>l>>r;
if(l>r)swap(l,r);
cout<<ask_sum(1,l,r)<<endl;
}
}
return 0;
}
数列分块 6
神人东西:rope。
就是平衡树,CCF 系列比赛可用。(亲身试过)
#include<bits/extc++.h>
using namespace std;
__gnu_cxx::rope<int> rp;
int main(){
int n;
cin>>n;
rp.push_back(9178);//1-index
for(int i=1;i<=n;i++){
int ai;
cin>>ai;
rp.push_back(ai);
}
while(n--){
int opt;
cin>>opt;
if(opt==0){
int l,r;
cin>>l>>r;
rp.insert(l,r);
}
else{
int c;
cin>>c;
cout<<rp[c]<<endl;
}
}
return 0;
}
数列分块 7
线段树2 板子。
#include<bits/stdc++.h>
#define int long long
#define ls id<<1
#define rs id<<1|1
int P;
#define endl '\n'
using namespace std;
const int maxn=400000+10;
struct Node{
int L,R;
int sum,add,mul;//mul:乘法
} t[maxn*4+1];
int n,a[maxn];
void up(int id){
t[id].sum=t[ls].sum+t[rs].sum;
}
void down(int id){//处理编号id的为标记,下传给它的儿子
t[ls].add=(t[ls].add*t[id].mul+t[id].add)%P;
t[rs].add=(t[rs].add*t[id].mul+t[id].add)%P;
t[ls].sum=(t[ls].sum*t[id].mul+(t[ls].R-t[ls].L+1)*t[id].add%P)%P;
t[rs].sum=(t[rs].sum*t[id].mul+(t[rs].R-t[rs].L+1)*t[id].add%P)%P;
t[ls].mul*=t[id].mul;t[ls].mul%=P;
t[rs].mul*=t[id].mul;t[rs].mul%=P;
t[id].add=0;t[id].mul=1;
}
void build(int id,int l,int r){//新建一个节点,编号为id,左端点为l,右端点为r
t[id].L=l;t[id].R=r;
if(l==r){//节点表示的区间为一个点,递归边界
t[id].sum=a[l];
return;
}
t[id].add=0,t[id].mul=1;
int mid=(l+r)>>1;
build(ls,l,mid);build(rs,mid+1,r);
t[id].sum=(t[ls].sum+t[rs].sum);t[id].sum%=P;
}
void update(int id,int l,int r,int val,string s){//将l,r区间同时加上一个val
if(s=="mul"){//mul标记
if(t[id].L>r||t[id].R<l) return;
if(t[id].L>=l&&t[id].R<=r){
t[id].add*=val;t[id].add%=P;
t[id].mul*=val;t[id].mul%=P;
t[id].sum*=val;t[id].sum%=P;
return;
}
if(t[id].add!=0||t[id].mul!=1)down(id);
update(ls,l,r,val,"mul");update(rs,l,r,val,"mul");
t[id].sum=(t[ls].sum+t[rs].sum);t[id].sum%=P;
return;
}
else{//add标记
if(t[id].L>r||t[id].R<l) return;
if(t[id].L>=l&&t[id].R<=r){
t[id].add+=val;t[id].add%=P;
t[id].sum+=(t[id].R-t[id].L+1)*val;t[id].sum%=P;
return;
}
if(t[id].add!=0||t[id].mul!=1)down(id);
update(ls,l,r,val,"add");update(rs,l,r,val,"add");
t[id].sum=(t[ls].sum+t[rs].sum);t[id].sum%=P;
return;
}
}
int ask_sum(int id,int l,int r){
if(t[id].L>r||t[id].R<l) return 0;
if(t[id].L>=l&&t[id].R<=r) return t[id].sum%P;
if(t[id].add!=0||t[id].mul!=1)down(id);
return (ask_sum(ls,l,r)+ask_sum(rs,l,r))%P;
}
signed main(){
int m,opt,l,r,val;
cin>>n;
P=10007;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(n--){
cin>>opt>>l>>r>>val;
if(opt==0){
update(1,l,r,val,"add");
} else if(opt==1){
update(1,l,r,val,"mul");
} else {
cout<<(ask_sum(1,r,r)%P+P)%P<<endl;
}
}
return 0;
}
数列分块 8
区间 \([l,r]\) 的推平以及查询区间 \([l,r]\) 内和 \(c\) 相等的数的个数。
思路
- 如果一个区间 \([l,r]\) 内所有数相等,为 \(x\):
-
- 如果 \(x=c\),则贡献为 \(r-l+1\)。
-
- 否则贡献为 0。
- 如果一个区间 \([l,r]\) 最小值大于 \(c\) 或最大值小于 \(c\),那么贡献为 0。
这是势能线段树的题目!
只需要维护最大值,最小值,覆盖操作即可。
#include<bits/stdc++.h>
//#define int long long
//#define endl '\n'
#define ls id<<1
#define rs id<<1|1
#define int long long
#define getchar getchar_unlocked
#define putchar putchar_unlocked
typedef long long ll;
using namespace std;
const ll maxn=3e5+10;
struct SegmentTree{
int l,r;
ll max,cov,min;
bool used;//used?
} t[maxn<<2];
int n,m;
ll a[maxn];
void up(int id){t[id].max=max(t[ls].max,t[rs].max),t[id].min=min(t[ls].min,t[rs].min);}
void down(int id){//下传
if((t[id].used)){
t[ls].cov=t[id].cov;
t[rs].cov=t[id].cov;
t[ls].max=t[id].cov;
t[rs].max=t[id].cov;
t[ls].min=t[id].cov;
t[rs].min=t[id].cov;
t[ls].used=t[rs].used=1;
}
t[id].cov=t[id].used=0;
}
void build(int id,int l,int r){//建立
t[id].l=l; t[id].r=r;
t[id].max=-1e18;
t[id].min=1e18;
if(l==r){
t[id].max=a[l];
t[id].min=a[l];
return;
}
int mid=(l+r)>>1;
build(ls,l,mid); //左子树
build(rs,mid+1,r);//右子树
up(id);
}
void change(int id,int l,int r,ll val){//updcov
if(t[id].l>r||t[id].r<l) return;
if(l<=t[id].l&&t[id].r<=r){
t[id].cov=val;
t[id].max=val;
t[id].min=val;
t[id].used=1;
return;
}
down(id);
change(ls,l,r,val);
change(rs,l,r,val);
up(id);
}//覆盖操作
int query(int id,int l,int r,int val){
if(t[id].l>r||t[id].r<l)return 0;
if(t[id].l==t[id].r)return t[id].max==val;
int Min=t[id].min,Max=t[id].max;
if(l<=t[id].l&&t[id].r<=r){
if(Min==Max){
return Min==val?t[id].r-t[id].l+1:0;
}
}
down(id);
if(Min>val||Max<val)return 0;
return query(ls,l,r,val)+query(rs,l,r,val);
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
cin>>n;
for(int i=1;i<=n;i++)cin>>a[i];
build(1,1,n);
while(n--){
int l,r,c;
cin>>l>>r>>c;
cout<<query(1,l,r,c)<<endl;
change(1,l,r,c);
}
return 0;
}
数列分块 9
蒲公英,但是没做。

浙公网安备 33010602011771号