洛谷P3960/loj2319 列队 Splay或线段树

让我们想想每次出队操作要做什么?本质上来看就是两种操作,寻找第k个人和把他放到末尾。
可以,这很Splay
又发现大多数人不会被挪动,所以我们可以把编号连续的人缩成一个点,如果我们要动其中的一个人,就把这个点拆成三个继续操作。
那么我们建立n+1棵Splay,其中n棵是处理每行的第1个人到第m-1个人建立,最后一棵是处理最后一列。
然后每次操作就是:
1.在第x行找到第y个人,拆点,删掉第y个人。
2.将最后一列的第x个人(也就是第x行m列的人)从列Splay中删掉,加入到第x行Splay末尾。
3.将原来的(x,y)加入到列Splay末尾。
这样理论上应该没什么大问题,但是由于litble自带一个大常数,所以只拿了70分。如果你松过去了,请联系litble告知你的Splay咋写的,谢谢。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
#define PR pair<int,LL>
typedef long long LL;
const int N=300005;
int n,m,Q,SZ,top;
int rt[N],s[N<<2][2],f[N<<2],rub[N<<2];LL l[N<<2],r[N<<2],sz[N<<2];
int is(int x) {return s[f[x]][1]==x;}
void up(int x) {sz[x]=sz[s[x][0]]+sz[s[x][1]]+r[x]-l[x]+1;}
int newnode() {
    if(top) return rub[top--];
    return ++SZ;
}
void spin(int x,int &mb) {
    int fa=f[x],g=f[fa],t=is(x);
    if(fa==mb) mb=x;
    else s[g][is(fa)]=x;
    f[fa]=x,f[x]=g,f[s[x][t^1]]=fa;
    s[fa][t]=s[x][t^1],s[x][t^1]=fa;
    up(fa),up(x);
}
void splay(int x,int &mb) {
    while(x!=mb) {
        if(f[x]!=mb) {
            if(is(x)^is(f[x])) spin(x,mb);
            else spin(f[x],mb);
        }
        spin(x,mb);
    }
}
void ins(int &x,int las,LL num) {//插入到末尾
    if(!x) {x=newnode(),l[x]=r[x]=num,f[x]=las,sz[x]=1;return;}
    ins(s[x][1],x,num),up(x);
}
PR getk(int x,LL kth) {//找第k个人所在的节点
    if(sz[s[x][0]]>=kth) return getk(s[x][0],kth);
    if(sz[s[x][0]]+r[x]-l[x]+1>=kth) return (PR){x,l[x]+kth-sz[s[x][0]]-1};
    return getk(s[x][1],kth-sz[s[x][0]]-(r[x]-l[x]+1));
}
void del(int x,int k) {//删除
    splay(x,rt[k]);
    if(s[x][0]*s[x][1]==0) rt[k]=s[x][0]+s[x][1];
    else {
        int y=s[x][1];
        while(s[y][0]) y=s[y][0];
        f[s[x][0]]=y,s[y][0]=s[x][0];
        while(y!=x) up(y),y=f[y];
        rt[k]=s[x][1];
    }
    f[rt[k]]=0,s[x][0]=s[x][1]=f[x]=0,rub[++top]=x;
}
void spilt(int x,LL num,int k) {//将x点分成[l[x],num-1][num,num][num+1,r[x]]三个点
    splay(x,rt[k]);
    if(l[x]==r[x]) del(x,k);
    else if(l[x]==num) ++l[x],up(x);
    else if(r[x]==num) --r[x],up(x);
    else {
        int now=newnode(),y=s[x][1];
        l[now]=num+1,r[now]=r[x],sz[now]=r[now]-l[now]+1,r[x]=num-1;
        if(y) {//将[num+1,r[x]]这个点插入到[l[x],num-1]后面
            while(s[y][0]) y=s[y][0];
            f[now]=y,s[y][0]=now;
        }
        else y=x,s[x][1]=now,f[now]=x;
        while(y) up(y),y=f[y];
    }
}
void work() {
    int x,y;PR tmp1,tmp2;
    while(Q--) {
        x=read(),y=read();
        tmp1=getk(rt[0],x),del(tmp1.first,0);
        if(y!=m) {
            tmp2=getk(rt[x],y),spilt(tmp2.first,tmp2.second,x);
            ins(rt[x],0,tmp1.second);
        }
        else tmp2=tmp1;
        ins(rt[0],0,tmp2.second),printf("%lld\n",tmp2.second);
    }
}
int main()
{
    n=read(),m=read(),Q=read();
    if(m>1) {
        for(RI i=1;i<=n;++i)
            rt[i]=++SZ,l[SZ]=(LL)(i-1)*m+1,r[SZ]=(LL)i*m-1,sz[SZ]=m-1;
    }
    for(RI i=1;i<=n;++i) ins(rt[0],0,(LL)i*m),splay(SZ,rt[0]);
    work();
    return 0;
}

既然Splay不够松,什么数据结构非常松松松呢?线段树!线段树常数那么小!
那么赶紧建立n+1棵线段树,含义和Splay一样。为了防止炸空间,要动态开点。
然后线段树中sz值表示这一段区间被“动”过的人有多少个,那么相对顺序不变的人就有(区间长-sz)个,我们可以利用这个信息去寻找第k个人。当然,第k个人有可能是被“动”过的人,我们可以开一个vector来存每一行除最后一列和最后一列末尾有哪些“零散”的人,如果发现第k个人被“动”过了,就在vector里找他。
然后再线段树修改,把从最后一列加入进来的人丢进vector,把(x,y)丢进最后一列的vector什么的。

#include<bits/stdc++.h>
using namespace std;
#define RI register int
int read() {
    int q=0;char ch=' ';
    while(ch<'0'||ch>'9') ch=getchar();
    while(ch>='0'&&ch<='9') q=q*10+ch-'0',ch=getchar();
    return q;
}
typedef long long LL;
const int N=300005;
int n,m,Q,SZ,lim,rt[N];
struct node{int l,r,sz;}tr[N*12];
vector<LL> orz[N];
int query(int kth,int s,int t,int x) {
    if(s==t) return s;
    int mid=(s+t)>>1,ksz=mid-s+1-tr[tr[x].l].sz;
    if(ksz>=kth) return query(kth,s,mid,tr[x].l);
    else return query(kth-ksz,mid+1,t,tr[x].r);
}
void chan(int s,int t,int &x,int pos) {
    if(!x) x=++SZ; ++tr[x].sz;
    if(s==t) return;
    int mid=(s+t)>>1;
    if(pos<=mid) chan(s,mid,tr[x].l,pos);
    else chan(mid+1,t,tr[x].r,pos);
}
LL kdel(int x,LL bh) {
    int t=query(x,1,lim,rt[0]);chan(1,lim,rt[0],t);
    LL re=(t<=n?1LL*t*m:orz[0][t-n-1]);
    orz[0].push_back(bh?bh:re);
    return re;
}
LL del(int x,int y) {
    int t=query(y,1,lim,rt[x]);chan(1,lim,rt[x],t);
    LL re=(t<m?1LL*m*(x-1)+t:orz[x][t-m]);
    orz[x].push_back(kdel(x,re));
    return re;
}
int main()
{
    int x,y;
    n=read(),m=read(),Q=read(),lim=max(n,m)+Q;
    while(Q--) {
        x=read(),y=read();
        if(y<m) printf("%lld\n",del(x,y));
        else printf("%lld\n",kdel(x,0));
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/litble/article/details/80663507