PAT

B1057 Stack(分块,树状数组+二分)

2020-01-23  本文已影响0人  Tsukinousag

B1057 Stack (30分)

数据范围N ≤10^5​​ ,模拟栈的push和pop的每个过程,求每个过程中栈内元素的中位数。
对于N次查询,使用快排去求中位数O( N * log2(N) ) * O(N)肯定会超时。

//树状数组不是顶级的考纲吗/(ㄒoㄒ)/~~

1.分块

单次查询中位数时间O(N^1/2)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
using namespace std;
typedef long long ll;
const int MAX=100005;
const int INF=0x3f3f3f3f;
const int mod=1000000007;
const int sqr=316;//sqrt(100001)向下取整,每块的最大容量。
stack<int>st;
int table[MAX];//hsah
int blocks[sqr];//每个块的当前元素数量
void peekmedian(int k)
{
    int sum=0;
    int idx=0;
    while(sum+blocks[idx]<k)
    {
        sum+=blocks[idx++];
    }
    int num=idx*316;//idx第一个数
    while(sum+table[num]<k)
    {
        sum+=table[num++];
    }
    printf("%d\n",num);
}
void push(int x)
{
      st.push(x);
      blocks[x/sqr]++;
      table[x]++;
}
void pop()
{
    int top=st.top();
    st.pop();
    blocks[top/sqr]--;
    table[top]--;
    printf("%d\n",top);
}
int main()
{
    int n;
    string s,a;
    memset(table,0,sizeof(table));
    memset(blocks,0,sizeof(blocks));
    scanf("%d",&n);
    getchar();
    for(int i=0;i<n;i++)
    {
        cin>>s;
        if(s=="Push")
        {
            int num;
            scanf("%d",&num);
            push(num);
        }
        else if(s=="Pop")
        {
          if(!st.empty())
          {
            pop();
          }
          else
            printf("Invalid\n");
        }
        else if(s=="PeekMedian")
        {
          if(!st.empty())
          {
            int k=st.size();
            int mid;
            if(k%2==1)
                mid=(k+1)/2;
            else
                mid=k/2;
            peekmedian(mid);
          }
          else
            printf("Invalid\n");
        }
    }

    return 0;
}

2.树状数组+二分

对一个序列,用hash记录每个元素出现的个数,那么序列第k大就是求最小的i使得sum(hash[1]~hash(i))>=k成立,用树状数组来解决hash数组求和问题,
等价于求第一个满足getsum(i)>=k的最小i
树状数组求和时间复杂度O(logn),二分查询i时间复杂度O(logn)
总时间O(logn)*O(logn)

#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <string.h>
#include <cmath>
#include <math.h>
#include <vector>
#include <queue>
#include <map>
#include <set>
#include <stack>
using namespace std;
typedef long long ll;
const int MAX=100005;
const int INF=0x3f3f3f3f;
const int mod=1000000007;
const int sqr=316;//sqrt(100001)向下取整,每块的最大容量。
#define lowbit(i)((i)&(-i))
stack<int>st;
int c[MAX];//树状数组
void update(int x,int v)
{
    for(int i=x;i<=MAX;i+=lowbit(i))
    {
        c[i]+=v;
    }
}
int getsum(int x)
{
    int sum=0;
    for(int i=x;i>0;i-=lowbit(i))
    {
        sum+=c[i];
    }
    return sum;
}
int peekmedian(int k)
{
    int l=1,r=MAX,mid;
    while(l<r)
    {
        mid=(l+r)/2;
        if(getsum(mid)>=k)
        {
            r=mid;
        }
        else
            l=mid+1;
    }
    return l;

}
void push(int x)
{
    st.push(x);
    update(x,1);
}
void pop()
{
    int top=st.top();
    st.pop();
    printf("%d\n",top);
    update(top,-1);
}
int main()
{
    int n;
    string s,a;
    memset(c,0,sizeof(c));
    scanf("%d",&n);
    getchar();
    for(int i=0;i<n;i++)
    {
        cin>>s;
        if(s=="Push")
        {
            int num;
            scanf("%d",&num);
            push(num);
        }
        else if(s=="Pop")
        {
          if(!st.empty())
          {
            pop();
          }
          else
            printf("Invalid\n");
        }
        else if(s=="PeekMedian")
        {
          if(!st.empty())
          {
            int k=st.size();
            int mid;
            if(k%2==1)
                mid=(k+1)/2;
            else
                mid=k/2;
            printf("%d\n",peekmedian(mid));
          }
          else
            printf("Invalid\n");
        }
    }

    return 0;
}

上一篇下一篇

猜你喜欢

热点阅读