模板系列---数据结构

P3378 【模板】堆

题目简述

给定三个操作,求数列中最小的数,删除数列中最小的数,插入一个新的数


思路

板子题没什么好说,用 stl 自带的优先队列很好写,但手写的也要掌握。
建议参考这篇博客


代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e6+6;
int tr[N];
int main(){
  int n,len=0;
  cin>>n;
  while(n--){
    int op;
    cin>>op;
    switch(op){
      case 1:{
        int x;
        cin>>x;
        tr[++len]=x;
        int kdl=len;
        while(kdl){
          if(tr[kdl>>1]>tr[kdl])tr[kdl>>1]^=tr[kdl]^=tr[kdl>>1]^=tr[kdl];
          else break;
          kdl>>=1;
        }
        break;
      }
      case 2:{
        cout<<tr[1]<<endl;
        break;
      }
      case 3:{
        int kdl=1;
        tr[len]^=tr[1]^=tr[len]^=tr[1];
        tr[len]=0;
        len--;
        while(kdl<len){
          int ls=(kdl<<1),rs=(kdl<<1|1);
          if(ls>len){
            break;
          }
          if(rs>len){
            if(tr[ls]<tr[kdl])tr[kdl]^=tr[ls]^=tr[kdl]^=tr[ls];
            break;
          }
          if(tr[ls]<tr[rs]){
            if(tr[ls]<tr[kdl])tr[kdl]^=tr[ls]^=tr[kdl]^=tr[ls],kdl=ls;
            else break;
          }
          else {
            if(tr[rs]<tr[kdl])tr[kdl]^=tr[rs]^=tr[kdl]^=tr[rs],kdl=rs;
            else break;
          }
        }
        break;
      }
    }
  }
  return 0;
}

PS:有时候真的很奇怪我是怎么能把代码都写这么冗长的/kk

P3865 【模板】ST 表

题目简述

对于给定的数列,要求以\(\theta(1)\)的时间复杂度计算出\([l_i,r_i]\)中最大值


思路

没什么可讲的,但要注意,计算区间长度的对数要是 log2(r-l+1) 不加1的话大多数情况下没问题,但是当 l=r 的时候会报错
BTW,用scanf printf 不会超时,用cin cout 是会超时的qwq
具体思路可以参考这个博客


代码

#include<bits/stdc++.h>
using namespace std;
const int N=1e5+5;
int a[N],s[N][25];
int n,m;
void in(int &x){
  x=0;
  int f=1;
  char c=getchar();
  while(c>'9'||c<'0'){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(c>='0'&&c<='9'){
    x=(x<<1)+(x<<3)+c-'0';
    c=getchar();
  }
  x*=f;
}
void pre_work(){
  for(int i=1;(1<<i)<=n&&i<25;i++){
    for(int j=1;j+(1<<i)-1<=n;j++){
      s[j][i]=max(s[j][i-1],s[j+(1<<(i-1))][i-1]);
    }
  }
}
void query(int l,int r){
  int k=log2(r-l+1);
  printf("%d\n",max(s[l][k],s[r-(1<<k)+1][k]));
}

int main(){
  in(n);in(m);
  for(int i=1;i<=n;i++)in(s[i][0]);
  pre_work();
  for(int i=1;i<=m;i++){
    int l,r;
    in(l);in(r);
    query(l,r);
  }
  return 0;
}

P3372 【模板】线段树 1

题目简述

对于一段数列,有如下两种操作

  1. 1 x y k:将区间 \([x, y]\) 内每个数加上 \(k\)
  2. 2 x y:输出区间 \([x, y]\) 内每个数的和。

思路

线段树裸树


代码

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=1e6+5;
ll a[N],n,m;
ll tr[N],tag[N];
void in(ll &x){
  x=0;
  int f=1;
  char c=getchar();
  while(c<'0'||c>'9'){
    if(c=='-')f=-1;
    c=getchar();
  }
  while(c>='0'&&c<='9'){
    x=(x<<1)+(x<<3)+c-'0';
    c=getchar();
  }
  x*=f;
}
ll ls(ll root){
  return root<<1;
}
ll rs(ll root){
  return root<<1|1;
}
void push_up(ll root){
  tr[root]=tr[ls(root)]+tr[rs(root)];
}
void work(ll l,ll r,ll root,ll k){
  tag[root]+=k;
  tr[root]+=(r-l+1)*k;
}
void push_down(ll l,ll r,ll root){
  ll mid=(l+r)>>1;
  work(l,mid,ls(root),tag[root]);
  work(mid+1,r,rs(root),tag[root]);
  tag[root]=0;
}
void build(ll root,ll l,ll r){
  ll mid=(l+r)>>1;
  if(l==r){
    tr[root]=a[l];
    return ;
  }
  build(ls(root),l,mid);
  build(rs(root),mid+1,r);
  push_up(root);
  return ;
}
void update(ll nl,ll nr,ll l,ll r,ll root,ll num){
  if(l>=nl&&r<=nr){
   tr[root]+=(r-l+1)*num; 
   tag[root]+=num;
   return ; 
  }
  //cout<<l<<' '<<r<<endl;
  push_down(l,r,root);
  ll mid=(l+r)>>1;
  if(mid>=nl)update(nl,nr,l,mid,ls(root),num);
  if(mid<nr)update(nl,nr,mid+1,r,rs(root),num);
  push_up(root);
  return ;
}
ll query(ll nl,ll nr,ll l,ll r,ll root){
  ll ans=0;
  if(l>=nl&&r<=nr)return tr[root];
  ll mid=(l+r)>>1;
  push_down(l,r,root); 
  if(mid>=nl)ans+=query(nl,nr,l,mid,ls(root));
  if(mid<nr)ans+=query(nl,nr,mid+1,r,rs(root));
  return ans;
}
int main(){
  freopen("3372.in","r",stdin);
  freopen("3372.out","w",stdout);
  in(n);in(m);
  for(int i=1;i<=n;i++)in(a[i]);
  build (1,1,n);
  for(int i=1;i<=m;i++){
    ll op;
    in(op);
    switch(op){
      case 1:{
        ll x,y,k;
        in(x);in(y);in(k);
        update(x,y,1,n,1,k);
        break;
      }
      case 2:{
        ll x,y;
        in(x);in(y);
        cout<<query(x,y,1,n,1)<<endl;
        break;
      }
    }
  }
  return 0;
}

P3373 【模板】线段树 2

题目简述

对于一段数列,有如下两种操作

  1. 1 x y k:将区间 \([x, y]\) 内每个数乘上 \(k\)
  2. 2 x y k:输出区间 \([x, y]\) 内每个数加上 \(k\)
  3. 3 x y :输出区间 \([x, y]\) 内每个数的和。

思路

线段树裸树

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=4e5+5;
ll a[N],tag_add[N],tag_mul[N],tr[N];
ll n,m,p;
inline ll ls(ll root){
  return root<<1;
}
inline ll rs(ll root){
  return root<<1|1;
}
void push_up(ll root){
  tr[root]=tr[ls(root)]+tr[rs(root)];
}
void build(ll root,ll l,ll r){
  tag_add[root]=0;
  tag_mul[root]=1;
  ll mid=(l+r)>>1;
  if(l==r){
    tr[root]=a[l];
    tr[root]%=p;
    return ;
  }
  build(ls(root),l,mid);
  build(rs(root),mid+1,r);
  push_up(root);
  return ;
}
void push_down(ll root,ll l,ll r){
  ll mid=(l+r)>>1;
  tr[ls(root)]=(tr[ls(root)]*tag_mul[root]+tag_add[root]*(mid-l+1))%p;
  tr[rs(root)]=(tr[rs(root)]*tag_mul[root]+tag_add[root]*(r-mid))%p;
  tag_mul[ls(root)]*=tag_mul[root];
  tag_mul[ls(root)]%=p;
  tag_mul[rs(root)]*=tag_mul[root];
  tag_mul[rs(root)]%=p;
  tag_add[ls(root)]=tag_add[ls(root)]*tag_mul[root]+tag_add[root];
  tag_add[ls(root)]%=p;
  tag_add[rs(root)]=tag_add[rs(root)]*tag_mul[root]+tag_add[root];
  tag_add[rs(root)]%=p;
  tag_add[root]=0;
  tag_mul[root]=1;
}
void mul(ll nl,ll nr,ll root,ll l,ll r,ll num){
  if(l>=nl&&r<=nr){
    tr[root]=(tr[root]*num)%p;
    tag_add[root]=(tag_add[root]*num)%p;
    tag_mul[root]=(tag_mul[root]*num)%p;
    return ;
  }
  push_down(root,l,r);
  ll mid=(l+r)>>1;
  if(mid>=nl) mul(nl,nr,ls(root),l,mid,num);
  if(mid+1<=nr) mul(nl,nr,rs(root),mid+1,r,num);
  push_up(root); 
  return ;
}
void add(ll nl,ll nr,ll root,ll l,ll r,ll num){
  if(l>=nl&&r<=nr){
    tr[root]=(tr[root]+num*(r-l+1))%p;
    tag_add[root]=(tag_add[root]+num)%p;
    return ;
  }
  push_down(root,l,r);
  ll mid=(l+r)>>1;
  if(mid>=nl) add(nl,nr,ls(root),l,mid,num);
  if(mid+1<=nr) add(nl,nr,rs(root),mid+1,r,num);
  push_up(root); 
  return ;
}
ll query(ll nl,ll nr,ll root,ll l,ll r){
  if(l>=nl&&r<=nr){
    return tr[root];
  }
  ll ans=0;
  push_down(root,l,r);
  ll mid=(l+r)>>1;
  if(mid>=nl) ans=(ans+query(nl,nr,ls(root),l,mid))%p;
  if(mid+1<=nr) ans=(ans+query(nl,nr,rs(root),mid+1,r))%p;
  return ans;
}
int main(){
  cin>>n>>m>>p;
  for(ll i=1;i<=n;i++)cin>>a[i];
  build(1,1,n);
  //cout<<tr[1]<<endl;
  for(ll i=1;i<=m;i++){
    ll op;
    cin>>op;
    switch(op){
      case 1:{
        ll x,y,k;
        cin>>x>>y>>k;
        mul(x,y,1,1,n,k);
        break;
      }
      case 2:{
        ll x,y,k;
        cin>>x>>y>>k;
        add(x,y,1,1,n,k); 
        break;
      }
      case 3:{
        ll x,y;
        cin>>x>>y;
        cout<<query(x,y,1,1,n)<<endl;;
        break;
      }/**/
    }
  }
  return 0;
}
posted @ 2022-12-20 23:20  paper_plane  阅读(19)  评论(0)    收藏  举报