zoukankan      html  css  js  c++  java
  • [uoj693]地铁规划

    问题即是要对一个栈支持:1.加入一个元素;2.删除最早加入的元素(各有$m$次)

    做法1(题解中的算法2)

    将栈中的元素标记为01,并按如下方式维护:

    1.对于加入操作,直接将其加入并标记为1

    2.对于删除操作,对其分类讨论——

    (1)若栈顶标记为0,直接弹出即可

    (2)若栈顶标记为1,不断弹出栈顶元素直至弹出的01标记个数相同(或栈为空)

    进一步的,将弹出的元素放回栈中,再对其分类讨论——

    a.若最终栈为空,则将所有标记为1的元素逆序放回并重新标记为0

    b.若最终栈不为空,则将所有标记为1的元素顺序放回

    不论哪一种情况,最终在将所有标记为0的元素顺序放回并弹出栈顶即可

    关于正确性,可以归纳元素的加入顺序为:从栈顶到栈顶标记为0的元素、从栈底到栈顶标记为1的元素,进而显然正确

    关于操作次数,可以以逆序对数(形如$(x,y)$满足$x$比$y$早插入而$x$更靠近栈底)除以$\sqrt{m}$为势能,考虑上述过程:

    1.显然均摊复杂度为$o(\sqrt{m})$

    2.(1)显然均摊复杂度为$o(1)$

    2.(2)假设弹出了$x$个1标记和$y$个0标记,那么实际复杂度为$o(x+y)$

    注意到1标记内是逆序的且第$i$个0标记前至少有$i$个1标记,因此本来至少有${x\choose 2}+\sum_{i=1}^{y}i$个逆序对

    2.(2).a其使得逆序对数为0,那么均摊复杂度为$o(x+y-\frac{x^{2}+y^{2}}{\sqrt{m}})=o(\sqrt{m})$

    2.(2).b其使得逆序对数为${x\choose 2}$且有$x=y$,那么均摊复杂度为$o(x-\frac{x^{2}}{\sqrt{m}})=o(\sqrt{m})$

    (上述已经包含了"将所有标记为0的元素顺序放回并弹出栈顶"的过程)

    另外,势能范围为$[0,m\sqrt{m}]$,因此势能差至多为$o(m\sqrt{m})$

    综上,总复杂度(操作次数)为$o(m\sqrt{m})$,由于常数优秀,可以通过

     1 #include<bits/stdc++.h>
     2 #include "subway.h"
     3 using namespace std;
     4 #define N 200005
     5 #define fi first
     6 #define se second
     7 int n,m,lim,ans;
     8 vector<int>v[2];
     9 stack<pair<int,int> >st;
    10 void init(int nn,int mm,int l){
    11     n=nn,m=mm,lim=l,ans=0;
    12     while (!st.empty())st.pop();
    13 }
    14 void add(int k,int p){
    15     merge(k),st.push(make_pair(k,p));
    16 }
    17 void del(){
    18     undo(),st.pop();
    19 }
    20 void Add(int k){
    21     add(k,1);
    22 }
    23 void Del(){
    24     if (!st.top().se){
    25         del();
    26         return;
    27     }
    28     v[0].clear(),v[1].clear();
    29     int x=1;
    30     v[1].push_back(st.top().fi);
    31     del();
    32     while ((!st.empty())&&(x)){
    33         x+=(st.top().se<<1)-1;
    34         v[st.top().se].push_back(st.top().fi);
    35         del();
    36     }
    37     if (st.empty()){
    38         for(int i=0;i<v[1].size();i++)add(v[1][i],0);
    39     }
    40     else{
    41         for(int i=(int)v[1].size()-1;i>=0;i--)add(v[1][i],1);
    42     }
    43     for(int i=(int)v[0].size()-1;i>=0;i--)add(v[0][i],0);
    44     del();
    45 }
    46 int solve(int k){
    47     while ((ans<m)&&(check(ans+1)))Add(++ans);
    48     Del();
    49     return ans;
    50 }
    View Code

     做法2(题解中的算法4,即标算)

    仍将栈中的元素标记为01,并按如下方式维护:

    1.对于加入操作,直接将其加入并标记为1

    2.对于删除操作,记$cnt$为栈中0标记的个数,若$cnt=0$则将全栈重构(翻转),并均标记为0

    进一步的,对其分类讨论:

    (1)若栈顶标记为0,直接弹出即可

    (2)若栈顶标记为1,弹出栈顶连续的一段标记为1的元素和之后的$lowbit(cnt)$个元素(必然均标记为0),然后先将1标记的元素顺序放回、再将所有0标记的元素

    关于正确性,可以归纳证明以下两点:

    1.元素的加入顺序为:从栈顶到栈顶0标记的元素、从栈底到栈顶1标记的元素

    2.从栈顶到栈底不断取出前$lowbit(cnt)$个标记为0的元素(注意$cnt$会因此变化),总是连续的

    进而显然正确

    关于操作次数,可以以"所有1标记后(到栈底)0标记素个数二进制下1的位数+1和"为势能(结合归纳的第2点),考虑上述过程:

    1.显然均摊复杂度为$o(\log m)$

    2.注意到这样会减少栈大小的势能,因此均摊复杂度为$o(1)$

    2.(1)显然均摊复杂度为$o(1)$

    2.(2)注意到弹出的标记为1的元素均会减少1的势能贡献,因此这一部分均摊复杂度为$o(1)$

    对于这$lowbit(cnt)$个元素,注意到$cnt$增长只有变为0时,而$\sum_{i=1}^{n}lowbit(i)\le \sum_{k=1}^{\log_{2}n}2^{k}\frac{n}{2^{k}}=o(n\log n)$,那么将所有累加起来后仍时$o(m\log m)$的

    另外,势能范围为$[0,m\log m]$,因此势能差至多为$o(m\log m)$

    综上,总复杂度(操作次数)为$o(m\log m)$(但实际表现甚至不如做法1),可以通过

     1 #include<bits/stdc++.h>
     2 #include "subway.h"
     3 using namespace std;
     4 #define N 200005
     5 #define fi first
     6 #define se second
     7 int n,m,lim,cnt,ans;
     8 vector<int>v[2];
     9 stack<pair<int,int> >st;
    10 void init(int nn,int mm,int l){
    11     n=nn,m=mm,lim=l,cnt=ans=0;
    12     while (!st.empty())st.pop();
    13 }
    14 int lowbit(int k){
    15     return (k&(-k));
    16 }
    17 void add(int k,int p){
    18     merge(k),st.push(make_pair(k,p));
    19 }
    20 void del(){
    21     undo(),st.pop();
    22 }
    23 void Add(int k){
    24     add(k,1);
    25 }
    26 void Del(){
    27     if (!cnt){
    28         v[1].clear();
    29         while (!st.empty()){
    30             v[1].push_back(st.top().fi);
    31             del();
    32         }
    33         for(int i=0;i<v[1].size();i++)add(v[1][i],0);
    34         cnt=st.size();
    35     }
    36     if (!st.top().se){
    37         del();
    38         return;
    39     }
    40     v[0].clear(),v[1].clear();
    41     while (st.top().se==1){
    42         v[1].push_back(st.top().fi);
    43         del();
    44     }
    45     for(int i=0;i<lowbit(cnt);i++){
    46         v[0].push_back(st.top().fi);
    47         del();
    48     }
    49     for(int i=(int)v[1].size()-1;i>=0;i--)add(v[1][i],1);
    50     for(int i=(int)v[0].size()-1;i>=0;i--)add(v[0][i],0);
    51     del();
    52 }
    53 int solve(int k){
    54     while ((ans<m)&&(check(ans+1)))Add(++ans);
    55     Del(),cnt--;
    56     return ans;
    57 }
    View Code
  • 相关阅读:
    eclipse修改web项目部署路径
    Jquery面试题
    23中设计模式之单例模式
    详细探讨单例模式
    java常用设计模式
    vue官网总结
    pytorch模型训练加速tricks
    element table显示滚动条
    vue中less文件全局引用
    vue路径别名无法识别,Cannot find module
  • 原文地址:https://www.cnblogs.com/PYWBKTDA/p/15707253.html
Copyright © 2011-2022 走看看