zoukankan      html  css  js  c++  java
  • 五一 DAY2

     DAY 2       2019.4.29


     比如:依次输入 3 1 5 4 2                                        插入 6

     

    这里FZdalao有一个很巧妙的构造,直接吧输入的数字排成一个二叉搜索树,方便以后操作

    把mid作为根节点,两边排

    void build2()      //精巧的构造,使得树高是log N的
    {
            sort(a+1,a+n+1);
            root=Divide(1,n);
            tot=n;
    }
    
    int Divide(int l,int r)
    {
             if (l>r) return 0;
            ls[mid]=Divide(l,mid-1);    //以mid为根节点 构造二叉搜索树 
            rs[mid]=Divide(mid+1,r);
            fa[ls[mid]]=fa[rs[mid]]=mid; fa[0]=0;
            sum[mid]=a[mid];
            size[mid]=size[ls[mid]]+size[rs[mid]]+1;  //树的大小 
            return mid;
    }

     

     

    说白了,删掉一个数,就用这个数左子树最大值或是右子树最小值替换他,并把父辈祖辈size-1

    size[x]表示x这个节点子树里节点的个数 

    1.简单情况

    int id=Find(x),t=fa[id];//找到这个点的编号 
            if (!ls[id]&&!rs[id])   //id没有孩子 
            {
                    if (ls[t]==id) ls[t]=0; else rs[t]=0; //去掉儿子边
                    for (i=id;i;i=fa[i]) size[i]--;   //id自己连同他的父辈大小全部-1 
            }

    2.id有一个孩子        我们删除7

        else
            if (!ls[id]||!rs[id])   //id有一个孩子 
            {
                    int child=ls[id]+rs[id];//找存在的儿子的编号 
                    if (ls[t]==id) ls[t]=child; else rs[t]=child;  //把id的儿子过继为父亲的儿子 
                    fa[child]=t;
                    for (i=id;i;i=fa[i]) size[i]--;    //id自己连同他的父辈大小全部-1 
            }

     3.他有两个孩子

    else //最复杂的情况         
     {
        for (i=fa[y];i;i=fa[i]) size[i]--;//注意到变换完之后y到root路径上每个点的size都减少了1
        int tt=fa[y]; //先把y提出来 
        if (ls[tt]==y)
        {
           ls[tt]=rs[y];
           fa[rs[y]]=tt;
        }                    
        else
        {
           rs[tt]=rs[y];
           fa[rs[y]]=tt;
        }    
    //再来提出x if (ls[t]==x) { ls[t]=y; fa[y]=t; ls[y]=ls[id]; rs[y]=rs[id]; } else { rs[t]=y; fa[y]=t; ls[y]=ls[id]; rs[y]=rs[id]; }
    size[y]
    =size[ls[y]]+size[rs[y]]+1;//更新一下size }

    int Findkth(int now,int k)
    {
            if (size[rs[now]]>=k) return Findkth(rs[now],k);
            else if (size[rs[now]]+1==k) return sum[now];
            else Findkth(ls[now],k-size[rs[now]]-1);//注意到递归下去之后右侧的部分都比它要大 
    }

    中序遍历,左头右

    每走到一个节点,先找左儿子,直到没有,再回到根(当前),再找右儿子

     

     

     


     

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<ctime>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    
    #define N 300005
    #define M 8000005
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    
    using namespace std;
    
    int i,j,m,n,p,k,a[N];
    
    int FindMin()   //找最小值 
    {
            return a[1];
    }
    
    void build1()   
    {
            sort(a+1,a+n+1);
    }
    
    void up(int now)   //上浮操作 
    {
            while (now&&a[now]<a[now/2]) swap(a[now],a[now/2]),now/=2;
    }
    
    void ins(int x)   //加入新数 
    {
            a[++n]=x; up(n);
    }
    
    void down(int now)  //下沉操作 
    {
            while (now*2<=n)   //保证不会超出树的规模 
            {
                    if (now*2==n)
                    {
                            if (a[now]>a[now*2]) swap(a[now],a[now*2]),now*=2; 
                    }
                    else
                    {
                            if (a[now]<=a[now*2]&&a[now]<=a[now*2+1]) break;
                            if (a[now*2]<a[now*2+1]) swap(a[now],a[now*2]),now*=2;  //与最小的儿子交换 
                            else swap(a[now],a[now*2+1]),now=now*2+1; 
                    }
            }
    }
    
    void del(int x)   //删除 
    {
            swap(a[x],a[n]); --n;
            up(x);
            down(x);
    }
    
    void change(int x,int val)    //交换数值 ,小则上浮,大则下沉 
    {
            if (a[x]>val)
            {
                a[x]=val;
                up(x);
            }
            else
            {
                a[x]=val;
                down(x);
            }
    }
    
    void build2()
    {
            for (i=n/2;i>=1;--i) down(i);
    }
    
    int main()
    { 
         scanf("%d",&n);
         for (i=1;i<=n;++i) scanf("%d",&a[i]);
         build2();
    }
        

     

     

    (这是个大根堆)

          

     

    set<int>st;   
    
    st.insert(x);     //插入 
    st.erase(x);      //删除 
    st.fnd(x);        //寻找 
    st.clear(x)       //清除 
    
    st.lower_bound(x);//  第一个>= x 的数的迭代器    
    st.upper_bound(x);//  第一个> x 的数的迭代器   
    st.begin(x);      //起点
    st.end(x) ;       //终点 
                      //只有以上这四个有迭代器, 因为他们是位置 
    
    
    
    set<int>::iterater it=st.lower_bound(x);   //迭代器 
                 ++it;//右移
                 --it;//左移
                 
    int x=*it;     //加星号,返回迭代器指向的实际数值


    PART 1

           

           所以ST表就是吧一个区间化解成两个区间,这两个区间合起来,保证全覆盖原区间

    这两个区间可以有重叠

           解决问题做到  重但不漏

     为什么要先求出每个[ i , i + 2k )的最值?

    因为你要把一个问题化为两个,并且你在预处理的时候并不知道所给的区间有多大

    证明一下全覆盖:

    设 k 为最大的保证 2r - l + 1 

    那么 2k+1 ≤ + 1

    实现了全覆盖

    若  2k+1  >= + 1

     那么k不是最大的,与假设相反

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<ctime>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    
    #define N 300005
    #define M 8000005
    #define K 18 
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    
    using namespace std;
    
    int i,j,m,n,p,k,ST[K+1][N],a[N],Log[N];
    
    int Find(int l,int r)
    {
            int x=Log[r-l+1];
            return max(ST[x][l],ST[x][r-(1<<x)+1]); //注意到对于[l,r],[l,l+2^x-1],[r-2^x+1,r]并起来是[l,r] 
    }
    
    int main()
    {
            scanf("%d",&n);
            for (i=1;i<=n;++i) scanf("%d",&a[i]);
            for (i=1;i<=n;++i) ST[0][i]=a[i];
            for (i=1;i<=K;++i)
                for (j=1;j+(1<<i)-1<=n;++j)
                        ST[i][j]=max(ST[i-1][j],ST[i-1][j+(1<<(i-1))]); //ST[i][j]为从j开始的长度为2^i的区间的最大值 
                                                            //显然[j,j+2^i)=[j,j+2^(i-1))+[j+2^(i-1),j+2^i)=max(ST[i-1][j],ST[i-1][j+2^(i-1)])
            for (i=1;(1<<i)<N;++i) Log[1<<i]=i; //令Log[x]为比x小的最大的2^y 
            for (i=1;i<N;++i) if (!Log[i]) Log[i]=Log[i-1];
            printf("%d
    ",Find(1,3));
    }

     PART 2

     

     

     

     

    [4,5]不需要继续拆

    #include<cstdio>
    #include<algorithm>
    
    #define N 100005
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    int tree[N*4];
    //
    T当前节点编号,l,r大区间长,tree标记为终止节点
    void modify(int ll,int rr,int c,int l,int r,int t)  //单点修改,看是往左走还是游走,或是叶子节点 
    {
            if (ll<=l&&r<=rr) 
            {
                    tree[t]=c;
                    return;
            }
            if (ll<=mid) modify(ll,rr,c,l,mid,ls);
            if (rr>mid)  modify(ll,rr,c,mid+1,r,rs);
            tree[t]=tree[ls]+tree[rs];
    }
     
    int S;
    
    void ask(int ll,int rr,int l,int r,int t)//[ll,rr]是要分解的区间 当前正分解到的节点编号是t,区间范围是[l,r] 
    {
            if (ll<=l&&r<=rr)    //出现区间[l,r]被[ll,rr]包含 
            {
                    S+=tree[t];
                    return;
            }
            if (ll<=mid) ask(ll,rr,l,mid,ls);   //走左边 
            if (rr>mid)  ask(ll,rr,mid+1,r,rs); //走右边 
    }
    
    int main()
    {
            modify(l,l,c,1,n,1);   //递归分解l,r
            S=0;
            ask(l,r,1,n,1);
    }

     

     

     

     

    先把标记放在一个大区间

    要继续向下拆分,就把标记传给儿子

    先不下传

    只有在需要用到的时候再下传子区间

    #include<cstdio>
    #include<algorithm>
    
    #define N 100005
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    int tree[N*4],Inc[N*4];
    
    void down(int t,int len) //下传点区间编号是t,长度是len 
    {
            //左儿子区间长度(len+1)/2,右儿子区间长度(len/2) 
            if (!Inc[t]) return;
            Inc[ls]+=Inc[t];    //区间整体加C
    
            tree[ls]+=Inc[t]*((len+1)>>1);
            Inc[rs]+=Inc[t];
            tree[rs]+=Inc[t]*(len>>1);
            Inc[t]=0;
    }
    
    void modify(int ll,int rr,int c,int l,int r,int t)//[ll,rr]是要分解的区间 当前正分解到的节点编号是t,区间范围是[l,r] 
    {
            if (ll<=l&&r<=rr) //当它是一个终止节点的时候,更新Inc标记和sum的值 
            {
                    tree[t]=c*(r-l+1);     //如果是整个区间被包含在内,就直接加inc,否则下传儿子down
    
                    Inc[t]+=c;
                    return;
            }
            down(t,r-l+1);
            if (ll<=mid) modify(ll,rr,c,l,mid,ls);
            if (rr>mid)  modify(ll,rr,c,mid+1,r,rs);
            tree[t]=tree[ls]+tree[rs];
    }
     
    int S;
    
    void ask(int ll,int rr,int l,int r,int t)//[ll,rr]是要分解的区间 当前正分解到的节点编号是t,区间范围是[l,r] 
    {
            if (ll<=l&&r<=rr)
            {
                    S+=tree[t];
                    return;
            }
            down(t,r-l+1);    //下传操作
            if (ll<=mid) ask(ll,rr,l,mid,ls);
            if (rr>mid)  ask(ll,rr,mid+1,r,rs);
    }
    
    int main()
    {
            modify(l,r,c,1,n,1);
            S=0;
            ask(l,r,1,n,1);
    }

    一共10000个砖块,20000个端点,其实可以看作40000块砖

    看成4n-1块砖,n表示地毯数目

     

     

     

    树状数组只能求前缀和

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<ctime>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    
    #define N 300005
    #define M 8000005
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    
    using namespace std;
    
    int i,j,m,n,p,k,C[N],a[N],b[N];  //C[i]维护区间和
    
    long long ans;
    
    int lowbit(int x)
    {
            return x&-x;  
    }
    
    void ins(int x,int y)//修改a[x]的值,a[x]+=y;
    {
            for (;x<=n;x+=lowbit(x)) C[x]+=y; //首先需要修改的区间是以x为右端点的,然后下一个区间是x+lowbit(x),以此类推 
    }
    
    int ask(int x) //查询[1,x]的值 
    {
            int ans=0;
            for (;x;x-=lowbit(x)) ans+=C[x]; //我们询问的]是[x-lowbit(x)+1,x,然后后面一个区间是以x-lowbit(x)为右端点的,依次类推 
            return ans;
    }
    
    int main()       
    {
            scanf("%d",&n);         //简单的离散化
            for (i=1;i<=n;++i) scanf("%d",&a[i]),b[++b[0]]=a[i];
            sort(b+1,b+b[0]+1); b[0]=unique(b+1,b+b[0]+1)-(b+1);   //unique对于已经排好顺序的数组去重,减去开头位置,得到里面元素的个数
            for (i=1;i<=n;++i) a[i]=lower_bound(b+1,b+b[0]+1,a[i])-b;   
    for (i=1;i<=n;++i) ans+=(i-1)-ask(a[i]),ins(a[i],1);
            printf("%lld
    ",ans);
    }

     PS:

     

     

     


     

                   

     

     

    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<ctime>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    
    #define N 300005
    #define M 8000005
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    
    using namespace std;
    
    int i,j,m,n,p,k,fa[N],deep[N];
    
    int get(int x) //不加任何优化的暴力 
    {
            while (fa[x]!=x) x=fa[x];
            return x;
    }
    
    //路径压缩
    /*
    int get(int x) { return fa[x]==x?fa[x]:fa[x]=get(fa[x]); }
    
    //按秩合并 
    
    void Merge(int x,int y)
    {
        x=get(x); y=get(y); 使用的是最暴力的get
        if (deep[x]<deep[y]) fa[x]=y;
        else if (deep[x]>deep[y]) fa[y]=x;
        else deep[x]++,fa[y]=x;
    }
    
    */ 
    
    void Merge(int x,int y)
    {
            x=get(x); y=get(y); //找到x,y所在连通块的顶点
            fa[x]=y; 
    }
    
    
    int Ask(int x,int y)
    {
            x=get(x); y=get(y);
            if (x==y) return 1;//表示连通 
            return 0; 
    }
    
    int main()
    {
            scanf("%d",&n);
            for (i=1;i<=n;++i) fa[i]=i,deep[i]=1;
            Merge(2,3);//合并2,3所在的连通块
            printf("%d
    ",Ask(2,3));//询问2,3是否在同一个连通块里 
    } 

    先拿出相等语句

     

     

          不存在Ai!=Aj   Ai!=Az   Aj!= Az

               


    #include<cstdio>
    #include<algorithm>
    #include<cstring>
    #include<iostream>
    #include<cstring>
    #include<string>
    #include<cmath>
    #include<ctime>
    #include<set>
    #include<vector>
    #include<map>
    #include<queue>
    
    #define N 300005
    #define M 8000005
    #define K 18
    
    #define ls (t<<1)
    #define rs ((t<<1)|1)
    #define mid ((l+r)>>1)
    
    #define mk make_pair
    #define pb push_back
    #define fi first
    #define se second
    
    using namespace std;
    
    int i,j,m,n,p,k,fa[N][K+1],deep[N];
    
    vector<int>v[N];
    
    void dfs(int x) //dfs求出树的形态,然后对fa数组进行处理 
    {
            int i;
            for (i=1;i<=K;++i) //fa[x][i]表示的是x向父亲走2^i步走到哪一个节点 
                fa[x][i]=fa[fa[x][i-1]][i-1]; //x走2^i步相当于走2^(i-1)步到一个节点fa[x][i-1],再从fa[x][i-1]走2^(i-1)步 
            for (i=0;i<(int)v[x].size();++i)
            {
                    int p=v[x][i];
                    if (fa[x][0]==p) continue;
                    fa[p][0]=x;
                    deep[p]=deep[x]+1; //再记录一下一个点到根的深度deep_x 
                    dfs(p);
            }
    }
    
    int Kth(int x,int k) //求第k个父亲,利用二进制位来处理 
    {
            for (i=K;i>=0;--i) //k可以被拆分成logN个2的整次幂 
                if (k&(1<<i)) x=fa[x][i];
            return x;
    }
    
    int Find_LCA(int x,int y) //求x,y的LCA 
    {
            int i,k;
            if (deep[x]<deep[y]) swap(x,y);
            x=Kth(x,deep[x]-deep[y]); //把x和y先走到同一深度 
            if (x==y) return x;
            for (i=K;i>=0;--i) //注意到x到根的路径是xa1a2...aic1c2...ck
                               //y到根的路径是        yb1b2...bic1c2...ck 我们要做的就是把x和y分别跳到a_i,b_i的位置,可以发现这段距离是相同的. 
                if (fa[x][i]!=fa[y][i]) x=fa[x][i],y=fa[y][i];
            return fa[x][0]; 
    }
    
    int main()
    {
        scanf("%d",&n);
        for (i=1;i<n;++i)
        {
                int x,y;
                scanf("%d%d",&x,&y);
                v[x].pb(y); v[y].pb(x);
        }
        dfs(1);
        printf("%d
    ",Find_LCA(3,5)); 
    }

  • 相关阅读:
    P4556 [Vani有约会]雨天的尾巴
    [模拟赛20180809] 旅程
    【jzoj3464】秀姿势
    【noip2013】火柴排队
    做运动
    【noip2013】花匠
    【noip2016】愤怒的小鸟
    【bzoj4326】【noip2015】运输计划
    作业二:个人编程项目——编写一个能自动生成小学四则运算题目的程序
    自我介绍
  • 原文地址:https://www.cnblogs.com/xiaoyezi-wink/p/10792377.html
Copyright © 2011-2022 走看看