zoukankan      html  css  js  c++  java
  • Weed:线段树

    观察复杂度,是log级别以下回答询问的。

    O(1)?逗我kx呢?

    自然而然地想到线段树。

    学长讲的原题啊考场上还不会打。

    线段树上的每个节点都表示一个操作区间。

    线段树上维护的权值有3个:这个子区间一共“净”加了多少层cnt,多少量num,以及它需要除掉前面的多少层del。

    因为对于每一个子区间,它只可能有几种情况:

    新累加了几层,或者这个子区间不够删了删前面的几层,或先删掉前面的几层再加上新的几层。

    那么每个叶节点就不用说啦,关键就在于两个区间合并。

    1,如果右区间要删左区间而且还能把它删干净,那么整个区间要删的量就是del[r]+del[l]-cnt[l],整个区间累加的量就是右区间所累加的

    2,如果删了但没删干净,那么我们就需要删掉左区间里最靠后的几个没被删除的值,删除量就是左区间的删除量,剩余层数就是cnt[l]+cnt[r]-del[r]

    但是怎么知道那“最靠后的几个值”呢?

    稍微改变一下设问,查询线段树子树中区间内最后几个有权值位置的权值和。打个ask函数就好了。

    然而这里有一个易错点:你不能直接查询左子树的最靠后的那几个值,因为右子树可能删除了它的一部分。

    所以你要查询的是p子树的后k个的话,实际上就是ask(p,k+del[bro])-ask(p,del[bro])

    bro是指p的右兄弟。如果p本身就是右子树则不存在。这样被它右子树删除的元素就被我们考虑到了。

    然后就到了考验码力的时候了。

     1 #include<bits/stdc++.h>
     2 using namespace std;
     3 #define lc p<<1
     4 #define rc p<<1|1
     5 int opt[200005],k[200005],num[800005],cnt[800005],del[800005],n,m,cl[800005],cr[800005];
     6 int ask(int p,int t){
     7     if(!t)return 0;
     8     if(cnt[p]<=t)return num[p];
     9     if(t<=cnt[rc])return ask(rc,t);
    10     return ask(lc,t-cnt[rc]+del[rc])-ask(lc,del[rc])+num[rc];
    11 }
    12 void up(int p){
    13     if(del[rc]>cnt[lc])del[p]=del[lc]+del[rc]-cnt[lc],cnt[p]=cnt[rc],num[p]=num[rc];
    14     else del[p]=del[lc],cnt[p]=cnt[rc]+cnt[lc]-del[rc],num[p]=num[rc]+num[lc]-ask(lc,del[rc]);
    15 }
    16 void build(int p,int l,int r){
    17     cl[p]=l;cr[p]=r;
    18     if(l==r){
    19         if(opt[l])del[p]=k[l];
    20         else cnt[p]=1,num[p]=k[l];
    21         return;
    22     }
    23     build(lc,l,l+r>>1);build(rc,(l+r>>1)+1,r);up(p);
    24 }
    25 void change(int p,int x){
    26     if(cl[p]==cr[p]){
    27         if(opt[x])del[p]=k[x],cnt[p]=num[p]=0;
    28         else cnt[p]=1,num[p]=k[x],del[p]=0;
    29         return;
    30     }
    31     if(cl[rc]<=x)change(rc,x);else change(lc,x);up(p);
    32 }
    33 int main(){
    34     scanf("%d%d",&n,&m);
    35     for(int i=1;i<=n;++i)scanf("%d%d",&opt[i],&k[i]);
    36     build(1,1,n);
    37     for(int i=1,x;i<=m;++i){
    38         scanf("%d",&x);scanf("%d%d",&opt[x],&k[x]);
    39         change(1,x);printf("%d
    ",num[1]);
    40     }
    41 }
    就1.1k但都是精华
  • 相关阅读:
    数据降维和可视化
    聚类
    支持向量机的实现
    支持向量机核函数
    支持向量基
    倾斜类误差度量
    构建垃圾邮件分类器
    POJ 2955 Brackets (区间dp入门)
    POJ 3126 Prime Path (bfs+欧拉线性素数筛)
    POJ 1426 Find The Multiple (dfs??!!)
  • 原文地址:https://www.cnblogs.com/hzoi-DeepinC/p/11332227.html
Copyright © 2011-2022 走看看