BZOJ 1901 洛谷 P2617 ZOJ 2112 Dynamic Rankings

如下時空限制來自zoj
Time limit 10000 ms
Memory limit 32768 kB
OS Linux
Source Online Contest of Christopher's Adventure
Author XIN, Taohtml

吐槽

zoj卡空間卡得太死了……32MB,遠遠不夠放沒優化過的主席樹套樹狀數組……剛開始時段錯誤,還覺得是t數組沒開夠,仍是學得不紮實,別人說是就是,都沒本身想一想算算……後來二分騙數據,發如今還沒開始操做、正在建最初的主席樹時空間就爆了。沒有優化的主席樹,t數組要開到N*100左右,但這題最多給開到N*40……因此逼着咱們優化空間。數組

平臺 n m 空間限制
洛谷 1e5 1e5 1000MB
BZOJ 1e4 1e4 128MB
ZOJ 5e4 1e4 32MB

解題思路

  1. 分塊+二分查找 https://blog.csdn.net/qq_35320178/article/details/81077787
  2. 總體二分、cdq分治 http://www.javashuo.com/article/p-uyyqswyw-bq.html
  3. 線段樹套平衡樹、權值線段樹套平衡樹 https://blog.csdn.net/htt_h/article/details/47701417
  4. 樹狀數組套主席樹 http://www.javashuo.com/article/p-btbaniee-ca.html
    • 這篇博客講得是真的好,垃圾百度把它排到賊靠後

源代碼

這個是我在BZOJ(本機測的darkbzoj數據,把N和M改小便可)、洛谷上的AC代碼,主要參考洛谷zsy的題解。ZOJ的留坑,打算把優化空間的方法、總體二分、cdq分治學了。函數

// luogu-judger-enable-o2
#include<stdio.h>
#include<string.h>
#include<algorithm>
const int N=100010,M=100010;
int T;
int n,m;
int a[N];
struct Opt{
    char mode;
    int l,r,k;
}opt[M];

int val[N+M],sz;
int id(int x){return std::lower_bound(val+1,val+sz+1,x)-val;}

struct Node{
    int lson,rson;
    long long sum;
}t[N*400];
int root[N],cnt=0;

int inline lowbit(int x){return x&-x;}

void update_tree(int &x,int l,int r,int pos,int k)
{//pre好像不須要啊,第x個節點的值並不用從pre繼承過來,由於套上樹狀數組之後就不是前綴和了。參考zsy博客
    if(!x) x=cnt++;//x等於0說明還沒分配過
    t[x].sum+=k;
    if(l==r) return;
    int mid=l+r>>1;
    if(pos<=mid) update_tree(t[x].lson,l,mid,pos,k);
    else update_tree(t[x].rson,mid+1,r,pos,k);
}
void change(int pos,int k)//數字a[pos]數量加k(先把以前的值去掉)
{
    int value=id(a[pos]);
    for(int i=pos;i<=n;i+=lowbit(i))
        update_tree(root[i],1,sz,value,k);
}
int temp[2][20];
//記錄詢問時須要統計的節點編號,由於根有root數組,能夠root[x-lowbit(x)],但下來的兒子孫子節點不能,因此先把須要統計的根記錄在temp數組中,而後統計完一層之後將temp數組記錄的節點向下挪動。
//temp[0]記錄第l-1棵線段樹上的節點編號,temp[1]記錄第r棵線段樹上的節點編號。
//因爲節點已經存到temp中了,因此que_tree函數的參數就少了不少了。
int que_tree(int l,int r,int k)
{
    if(l==r) return l;
    int mid=l+r>>1,delta=0;
    for(int i=1;i<=temp[1][0];i++) delta+=t[t[temp[1][i]].lson].sum;
    for(int i=1;i<=temp[0][0];i++) delta-=t[t[temp[0][i]].lson].sum;//統計那幾棵樹的左兒子的sum,用來和k比較,找出從左仍是從右
    if(k<=delta)//向左走
    {
        for(int i=1;i<=temp[0][0];i++) temp[0][i]=t[temp[0][i]].lson;
        for(int i=1;i<=temp[1][0];i++) temp[1][i]=t[temp[1][i]].lson;//temp也向下走
        return que_tree(l,mid,k);
    }
    else
    {
        for(int i=1;i<=temp[0][0];i++) temp[0][i]=t[temp[0][i]].rson;
        for(int i=1;i<=temp[1][0];i++) temp[1][i]=t[temp[1][i]].rson;
        return que_tree(mid+1,r,k-delta);
    }
    
}

int que(int l,int r,int k)
{
    temp[0][0]=temp[1][0]=0;//用於統計temp[0]和temp[1]中存了多少個節點下標
    for(int i=l-1;i;i-=lowbit(i))
        temp[0][++temp[0][0]]=root[i];
    for(int i=r;i;i-=lowbit(i))
        temp[1][++temp[1][0]]=root[i];
    return que_tree(1,sz,k);
}
/******************調試代碼******************/
//用於輸出主席樹上每棵線段樹所表明的序列
int quesum(int x,int l,int r,int pos)
{
    if(l==pos&&pos==r) return t[x].sum;
    int mid=l+r>>1;
    if(pos<=mid) return quesum(t[x].lson,l,mid,pos);
    else return quesum(t[x].rson,mid+1,r,pos);
}
inline void debug()
{
    for (int i = 1; i <= sz; i++)
        printf("%d ", val[i]);
    puts("\n****************");
    for (int rt = 0; rt <= n; rt++, puts(""))
        for (int i = 1; i <= sz; i++)
            printf("%d ", quesum(root[rt], 1, sz, i));
    puts("******************");
}
/*******************調試代碼完*****************/
int main()
{
    //freopen("test.in","r",stdin);
    //scanf("%d",&T);
    //while(T--)
    {
        sz=0;
        scanf("%d%d",&n,&m);
        for(int i=1;i<=n;i++) scanf("%d",a+i),val[++sz]=a[i];
        for(int i=1;i<=m;i++)
        {
            char mode[2];
            int l,r,k;
            scanf("%s",mode);
            if(mode[0]=='Q')
            {
                scanf("%d%d%d",&l,&r,&k);
                opt[i]={mode[0],l,r,k};
            }
            else
            {
                scanf("%d%d",&l,&k);
                opt[i]={mode[0],l,0,k};
                val[++sz]=k;
            }
        }

        std::sort(val+1,val+1+sz);
        sz=std::unique(val+1,val+sz+1)-val-1;
        
        //memset(root,0,sizeof(root));
        //memset(t,0,sizeof(t));
        cnt=1;
        for(int i=1;i<=n;i++)
            change(i,1);
        //debug();
        for(int i=1;i<=m;i++)
        {
            if(opt[i].mode=='Q')
            {
                printf("%d\n",val[que(opt[i].l,opt[i].r,opt[i].k)]);
            }
            else
            {
                change(opt[i].l,-1);
                a[opt[i].l] = opt[i].k;
                change(opt[i].l,1);
                //debug();
            }
        }
    }
    return 0;
}
相關文章
相關標籤/搜索