2016年1月20日 星期三

[TIOJ 1840]Coding Days コーディングデイス


這題彩蛋還真多......例如把滑鼠移到某個"coding days"會突然跳出一張圖片XD

這題可以用線段樹來維護,每個節點存的是經過排序後的數列,這樣每次詢問時就可以直接對值域做二分搜,把相關的$\log N$個節點找出來相加算出這個值的排名,當作二分搜的比較基準

由於要支持修改操作,因此每個節點的有序數列不能直接用陣列,否則修改會TLE。這裡我用Treap實現名次樹,因此整個資料結構就是「線段樹套名次樹」

至於插入操作,題目再三叮囑你要詳讀題目敘述
題目敘述中可以看到這一行:「......我們相信如果紀錄裡出現了插入的動作,我們也只要輸出一行7122......」
所以如果出現操作3,直接輸出一行"7122"就好了(還好CBD有提醒不然我就!@#%^&*...

對了先將值域離散化可以加速,像我的code就可以用1950ms的時間踩過2000ms的限制(?)

設值域大小為$M$
初始化複雜度:$O(N\log M)$
操作1複雜度:$O(\log N{\log}^{2}M)$
操作2複雜度:$O(\log N\log M)$
操作3複雜度:$O(1)$
總時間複雜度:$O(Q\log N{\log}^{2}M)$

code:
#include<cstdio>
#include<cassert>
#include<vector>
#include<algorithm>
using namespace std;
//void assert(bool valid){for(;;)putchar('E');}
struct QueryType{int type,l,r,v;};
struct Node
{
    Node *ch[2];
    int v,cnt,sz,sum;
    Node(const int _v):ch{NULL,NULL},v(_v),cnt(1),sz(1),sum(1){}
};
void Delete(Node* &o)
{
    if(!o)return;
    Delete(o->ch[0]),Delete(o->ch[1]);
    delete o;o=NULL;
}
int Sz(Node* &o){return o?o->sz:0;}
int Sum(Node* &o){return o?o->sum:0;}
void Maintain(Node* &o)
{
    o->sz=Sz(o->ch[0])+1+Sz(o->ch[1]);
    o->sum=Sum(o->ch[0])+(o->cnt)+Sum(o->ch[1]);
}
void Rotate(Node* &o,const int cu)
{
    Node *u=o;
    o=o->ch[cu];
    u->ch[cu]=o->ch[cu^1];
    o->ch[cu^1]=u;
    Maintain(u),Maintain(o);
}
void Rotate(Node* &o)
{
    if(Sz(o->ch[0])>Sz(o->ch[1]))Rotate(o,0);
    else if(Sz(o->ch[1])>Sz(o->ch[0]))Rotate(o,1);
}
void Insert(Node* &o,const int v)
{
    if(!o){o=new Node(v);return;}
    if((o->v)==v){o->cnt++;Maintain(o);return;}
    Insert(o->ch[v<(o->v)?0:1],v);
    Maintain(o),Rotate(o);
}
void Erase(Node* &o)
{
    if(!o->ch[0]||!o->ch[1])
    {
        Node *u=o;
        o=o->ch[o->ch[0]?0:1];
        delete u;return;
    }
    const int cu=(Sz(o->ch[0])>Sz(o->ch[1])?0:1);
    Rotate(o,cu);
    Erase(o->ch[cu^1]);
    Maintain(o);
}
void Erase(Node* &o,const int v)
{
    assert(o);
    if((o->v)==v)
    {
        (o->cnt)--;
        Maintain(o);
        if((o->cnt)==0)Erase(o);
        return;
    }
    Erase(o->ch[v<(o->v)?0:1],v);
    Maintain(o);
}
int N,S[50000];
Node *ST[200000];
void Build(const int id,const int l,const int r)
{
    Delete(ST[id]);
    for(int i=l;i<=r;i++)Insert(ST[id],S[i]);
    assert(Sum(ST[id])==r-l+1);
    if(l==r)return;
    const int mid=(l+r)/2;
    Build(id*2,l,mid),Build(id*2+1,mid+1,r);
}
void Modify(const int id,const int l,const int r,const int loc,const int v)
{
    Erase(ST[id],S[loc]),Insert(ST[id],v);
    assert(Sum(ST[id])==r-l+1);
    if(l==r)return;
    const int mid=(l+r)/2;
    if(loc<=mid)Modify(id*2,l,mid,loc,v);
    else Modify(id*2+1,mid+1,r,loc,v);
}
int QueryRank(Node* &o,const int v)
{
    if(!o)return 0;
    if((o->v)<=v)return Sum(o->ch[0])+(o->cnt)+QueryRank(o->ch[1],v);
    else return QueryRank(o->ch[0],v);
}
int QueryRank(const int id,const int l,const int r,const int bl,const int br,const int v)
{
    if(r<bl||br<l)return 0;
    if(bl<=l&&r<=br)
    {
//        printf("(%d,%d,%d,%d,%d)=%d(sz=%d,cnt=%d,sum=%d)\n",l,r,bl,br,v,QueryRank(ST[id],v),Sz(ST[id]),ST[id]->cnt,ST[id]->sum);
        return QueryRank(ST[id],v);
    }
    const int mid=(l+r)/2;
    return QueryRank(id*2,l,mid,bl,br,v)+QueryRank(id*2+1,mid+1,r,bl,br,v);
}
vector<QueryType>QUERY;
//void Print(Node* &o)
//{
//    if(!o)return;
//    Print(o->ch[0]);
//    printf("{%d,%d}",o->v,o->cnt);
//    Print(o->ch[1]);
//}
//void Print(const int id,const int l,const int r)
//{
//    printf("(%d,%d):",l,r);Print(ST[id]);puts("");
//    if(l==r)return;
//    const int mid=(l+r)/2;
//    Print(id*2,l,mid),Print(id*2+1,mid+1,r);
//}
int main()
{
//    freopen("in.txt","r",stdin);
    for(int i=0;i<200000;i++)ST[i]=NULL;
    int testcount;scanf("%d",&testcount);
    while(testcount--)
    {
        int querycount;
        scanf("%d%d",&N,&querycount);
        vector<int>vals;
        for(int i=0;i<N;i++)scanf("%d",&S[i]),vals.push_back(S[i]);
        Build(1,0,N-1);
        QUERY.clear();
        for(QueryType q;querycount--;)
        {
            scanf("%d",&q.type);
            switch(q.type)
            {
                case 1:scanf("%d%d%d",&q.l,&q.r,&q.v),q.l--,q.r--;break;
                case 2:scanf("%d%d",&q.l,&q.v),q.l--;vals.push_back(q.v);break;
                case 3:scanf("%d%d",&q.l,&q.v),q.l--;break;
                default:assert(0);break;
            }
            QUERY.push_back(q);
        }
        sort(vals.begin(),vals.end()),vals.resize(unique(vals.begin(),vals.end())-vals.begin());
        for(const QueryType &q:QUERY)
        {
//            printf("%d %d %d %d\n",q.type,q.l,q.r,q.v);
            switch(q.type)
            {
                case 1:
                {
                    int l=0,r=vals.size();
                    while(l<r)
                    {
                        const int mid=(l+r)/2;
                        const int result=QueryRank(1,0,N-1,q.l,q.r,vals[mid]);
//                        printf("(%d,%.d,%d):val=%d,rank=%d\n",q.l,q.r,q.v,vals[mid],result);
                        if(result>=q.v)r=mid;
                        else l=mid+1;
                    }
                    assert(r<(int)vals.size());
                    printf("%d\n",vals[r]);
                }break;
                case 2:Modify(1,0,N-1,q.l,q.v);/*printf("Modify(%d,%d)\n",q.l,q.v),*/S[q.l]=q.v;break;
                case 3:puts("7122");break;
                default:assert(0);break;
            }
//            Print(1,0,N-1);
        }
    }
    return 0;
}

沒有留言:

張貼留言

歡迎留言或問問題~
若您的留言中包含程式碼,請參考這篇
如果留言不見了請別慌,那是因為被google誤判成垃圾留言,小莫會盡快將其手動還原