列队[noip2017]

链接

其实从我开始学splay后,我就想过开n棵spaly硬钢这道题

后来我又想到动态开点

可惜我不会

终于今天,嗯,我A了它,真好

#include<bits/stdc++.h>
#define re return
#define LL long long
#define inc(i,l,r) for(LL i=l;i<=r;++i)

using namespace std;
template<typename T>inline void rd(T&x)
{
    char c;bool f=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
    x=c^48;
    while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
    if(f)x=-x;
}

const int maxn=3000000;
LL n,m,tot,cnt,Q;
int fa[maxn],son[maxn][2];
LL size[maxn],ll[maxn],rr[maxn];

/*此题区间左闭右开*/
struct Splay
{
    int rt;
    //建新点 
    inline int New_point(LL l,LL r)
    {
        ++cnt;
        fa[cnt]=son[cnt][0]=son[cnt][1]=0;
        ll[cnt]=l;rr[cnt]=r;size[cnt]=r-l;
        re cnt;
    }
    //saply常规操作
    inline int chk(int x){re son[fa[x]][1]==x;}
    inline void pushup(int x)
    {
        size[x]=size[son[x][0]]+size[son[x][1]]+rr[x]-ll[x];
    } 
    
    inline void rotate(int x)
    {
        int y=fa[x],z=fa[y],f=chk(x),w=son[x][f^1];
        son[z][chk(y)]=x;fa[x]=z;
        son[x][f^1]=y;fa[y]=x;
        son[y][f]=w;fa[w]=y;
        pushup(y);
        pushup(x);
    }
    
    inline void splay(int x,int goal=0)
    {
        while(fa[x]!=goal)
        {
            int y=fa[x],z=fa[y];
            if(z!=goal)
            chk(x)==chk(y)?rotate(y):rotate(x);
            rotate(x);
        }
        if(!goal)rt=x;
    }
    
    //动态开点平衡树 (将n多操作合二为一) 

    //你从未见过的船新操作,动态开点
    //将前k个点放入原根,后面新开节点 
    inline LL Split_point(int u,LL k)
    {
        k+=ll[u];
        int y=New_point(k,rr[u]);
        rr[u]=k;
        if(!son[u][1])
            fa[son[u][1]=y]=u;
        else 
        {
            int t=son[u][1];
            while(son[t][0])t=son[t][0];
            fa[son[t][0]=y]=t;
            while(t!=u)pushup(t),t=fa[t];
        }
        splay(y);
        re y;
    } 
    
    //找出第K大点并弹出 
     LL Pop_kth(LL k)
    {
        int u=rt;
        while(2333)
        {
            int y=son[u][0];
            if(size[y]>=k)u=y;
            else 
            {
                k-=size[y];
                if(rr[u]-ll[u]<k)k=k-(rr[u]-ll[u]),u=son[u][1];
                else 
                {
                    if(k!=rr[u]-ll[u])Split_point(u,k);
                    if(k!=1)u=Split_point(u,k-1);
                    break;
                } 
            }
        }
        
        splay(u);
        fa[son[u][0]]=fa[son[u][1]]=0;
        if(!son[u][0])
        {
            rt=son[u][1];
        }
        else
        {
            int t=son[u][0];
            while(son[t][1])t=son[t][1];
            splay(t);
            rt=t;
            fa[son[t][1]=son[u][1]]=t;
            pushup(t);
        }
        re ll[u]; 
    }
    
    
    //找到最后,并放进新数 
    inline void Push_back(LL x)
    {
        int p=New_point(x,x+1);
        if(!rt)    rt=p;
        else
        {
            int u=rt;
            while(son[u][1])
                u=son[u][1];
            splay(u); 
            son[fa[p]=u][1]=p;
            pushup(u);
        }
    }
    
}s[maxn];

int main()
{
    //freopen("in.txt","r",stdin);
    freopen("testdata.in","r",stdin);
    LL x,y,p;
    
    rd(n),rd(m),rd(Q);
    
    //建树 
    inc(i,1,n) s[i].rt=s[i].New_point((i-1)*m+1,i*m);
    s[0].rt=s[0].New_point(m,m+1);
    inc(i,2,n)
        s[0].Push_back(i*m);
    
    inc(i,1,Q)
    {
        rd(x),rd(y);
        s[x].Push_back(s[0].Pop_kth(x));
        printf("%lld\n",p=s[x].Pop_kth(y));    
        s[0].Push_back(p);
    }  
    
    re 0;
} 

 

 

当然,动态开点权值线段树什么的最适合这道题了

/*
魔改程序伤不起
LL落下两行泪 
*/
#include<bits/stdc++.h>
#define re return
#define ll long long
#define inc(i,l,r) for(ll i=l;i<=r;++i)

using namespace std;
template<typename T>inline void rd(T&x)
{
    char c;bool f=0;
    while((c=getchar())<'0'||c>'9')if(c=='-')f=1;
    x=c^48;
    while((c=getchar())>='0'&&c<='9')x=x*10+(c^48);
    if(f)x=-x;
}

const int maxn=7000000;
ll n,m,tot,cnt,Q,flag,ans;
ll x,y;
ll use[maxn],ls[maxn],rs[maxn],num[maxn];
ll sum[300005],T[300005];

inline void query(ll &rt,ll l,ll r,ll k)
{
    if(!rt)rt=++cnt;
    //没有访问过 开点 
    ++use[rt];
    //因为下面用的是use[ls[rt]],没有影响 
    if(l==r)
    {
        if(num[rt])ans=num[rt];
        else if(!flag)ans=l*m;
        else ans=(x-1)*m+l;
        re ;
    }
    
    ll mid=(l+r)>>1;
    ll cur=mid-l+1-use[ls[rt]];
    //标记真实存在的个数 
    if(cur>=k)query(ls[rt],l,mid,k);
    else query(rs[rt],mid+1,r,k-cur);
    re ;
}

inline void insert(ll &rt,ll l,ll r,ll pos)
{
    if(!rt)rt=++cnt;
    //没有访问过 开点 
    if(l==r)
    { 
        num[rt]=ans;
        re;
    }    
    
    ll mid=(l+r)>>1;
    if(pos<=mid)insert(ls[rt],l,mid,pos);
    else insert(rs[rt],mid+1,r,pos);
}
int main()
{
    //freopen("in.txt","r",stdin);
    freopen("testdata.in","r",stdin);
    ll p,t;
    
    rd(n),rd(m),rd(Q);
    inc(i,1,Q)    
    {
        rd(x),rd(y);
        if(y==m)
        {
            flag=0;
            query(T[n+1],1,n+Q,x);
            printf("%lld\n",ans);
            ++sum[n+1];//现在放入位置+n
            insert(T[n+1],1,n+Q,sum[n+1]+n);
       //预留好足够大的位置
//加入末尾 } else { flag=x; query(T[x],1,m+Q,y); printf("%lld\n",ans); ++sum[n+1]; insert(T[n+1],1,n+Q,sum[n+1]+n); flag=0; query(T[n+1],1,n+Q,x); ++sum[x]; insert(T[x],1,m+Q,sum[x]+m-1); //此处之所以,是因为每一行的m列不被包括在那一行中
      //将每一行中的m抹去
} } re 0; }

 

 我太蒟蒻了,还没理解树状数组的做法,待我有时

posted @ 2019-08-22 20:08  凉如水  阅读(173)  评论(0编辑  收藏  举报