E. Copying Data 解析(線段樹)

Codeforce 292 E. Copying Data 解析(線段樹)

今天我們來看看CF292E
題目連結

題目
給你兩個陣列\(a,b\),有兩種操作:把\(a\)的一段複製到\(b\),或者查詢\(b\)的某個元素是什麼。

前言

去看其他人Submit的code會發現有其他做法,我的作法可能不是最好的,畢竟我對於單點查詢且無法維護區間值的線段樹不是很熟。

想法

首先察覺到這題有可能是線段樹,畢竟要維護一堆區間,然後還要查詢。
但是開始寫以後會察覺到:這是單點查詢,且我(們)想不到該維護什麼狀態才能夠把兩個不同區間的值合併到更大的區間中。我們本質上來說只是想要維護有哪些區間被修改而已,因此我們想到用懶標記(區間修改線段樹),「並且查詢時只查詢長度為一的區間」,也就是說,只有長度為一的區間所維護的值是有意義的,其他長度更長的區間只有維護的懶標記是有意義的。
因此,我們在線段樹的懶標記維護三個數字:\(\{x,y,k\}\),也就是題目給的三個數字,而長度為一的區間的值就是最後一次複製上去的\(\{x,y,k\}\)

程式碼:

const int _n=1e5+10;
int tt,n,m,a[_n],b[_n],x,y,k;
struct M{int x,y,k;};
M res;
namespace Seg{
  int nn;
  M t[_n<<2],laz[_n<<2];
  void pull(int v){}
  void apply(int v, M val){t[v]=val,laz[v]=val;}
  void push(int v){
    if(laz[v].k!=0)apply(2*v+1,laz[v]),apply(2*v+2,laz[v]),laz[v]={0,0,0};
  }
  void build(int v, int l, int r){
    if(l+1==r)t[v]={0,0,0};
    else{int m=(l+r)>>1;build(2*v+1,l,m),build(2*v+2,m,r);pull(v);}
  }
  void add(int v,int l,int r,int ql,int qr,M val){
    if(r<=ql or qr<=l)return;
    else if(ql<=l and r<=qr)apply(v,val);
    else{
      push(v);int m=(l+r)>>1;
      add(2*v+1,l,m,ql,qr,val),add(2*v+2,m,r,ql,qr,val);
      pull(v);
    }
  }
  void add(int l,int r,M val){add(0,0,nn,l,r,val);}
  void add(int pos,M val){add(0,0,nn,pos,pos+1,val);}
  void init(int n_){nn=n_;build(0,0,nn);}
  void chk(int v,int l,int r,int pos){
    int m=(l+r)>>1;if(l+1==r){res=t[v];return;}
    push(v);
    if(pos<m)chk(2*v+1,l,m,pos);
    else chk(2*v+2,m,r,pos);
  }
}
//上面是模板修改來的,所以函式名叫做add
main(void) {ios_base::sync_with_stdio(0);cin.tie(0);cout.tie(0);
  cin>>n>>m;rep(i,0,n)cin>>a[i];rep(i,0,n)cin>>b[i];
  Seg::init(n);
  while(m--){
    cin>>tt;
    if(tt==1){
      cin>>x>>y>>k;x--,y--;
      Seg::add(y,y+k,{x,y,k});
    }
    if(tt==2){
      cin>>x;x--;
      Seg::chk(0,0,n,x);
      if(res.k!=0)cout<<a[res.x+(x-res.y)]<<'\n';
      else cout<<b[x]<<'\n';
    }
  }
  return 0;
};

標頭、模板請點Submission看
Submission

posted @ 2020-08-28 11:55  petjelinux  阅读(215)  评论(0编辑  收藏  举报