/ Vijos / 题库 / 列队 /

题解

4 条题解

  • 3
    @ 2018-01-26 22:14:31
    #include <cmath>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <iomanip>
    #include <iostream>
    #include <algorithm>
    #include <vector>
    #include <deque>
    #include <set>
    #include <limits>
    #include <string>
    #include <sstream>
    using namespace std;
    
    namespace dts
    {
        typedef long long ll;
        
        //splay
        ll size;
        ll fa[3000000+1];
        ll s[3000000+1][1+1];
        ll sl[3000000+1];
        ll sr[3000000+1];
        ll len[3000000+1];
        //splay
        
        struct splay
        {
            ll root;
            
            ll insert(ll l,ll r)
            {
                size++;
                fa[size]=s[size][0]=s[size][1]=0;
                len[size]=(sr[size]=r)-(sl[size]=l);
                //interval  [l,r) [l,r-1]
                return size;
            }
            
            ll pr(ll l,ll r)
            {
                return (root=insert(l,r));
            }
            
            ll update(ll now)
            {
                return (len[now]=len[s[now][0]]+len[s[now][1]]+sr[now]-sl[now]);
            }
            
            ll dir(ll now)
            {
                //left or right
                return ((s[fa[now]][1]==now)?1:0);
            }
            
            void rotate(ll now)
            {
                ll nowdir=dir(now),nowfa=fa[now];
                fa[now]=fa[nowfa];
                if (nowfa==root)
                    root=now;
                else
                    s[fa[nowfa]][dir(nowfa)]=now;
                if ((s[nowfa][nowdir]=s[now][nowdir^1])!=0)
                    fa[s[nowfa][nowdir]]=nowfa;
                fa[s[now][nowdir^1]=nowfa]=now;
                update(nowfa);
                update(now);
            }
            
            void splay_work(ll now)
            {
                for (;fa[now];rotate(now))
                    if (fa[fa[now]]!=0)
                        rotate((dir(now)==dir(fa[now]))?fa[now]:now);
            }
            
            ll split(ll now,ll x)
            {
                x+=sl[now];
                //pre x number not change
                //behind number move to new node
                ll ans=insert(x,sr[now]);
                //ans   new node
                sr[now]=x;
                if (s[now][1]==0)
                    fa[s[now][1]=ans]=now;
                else
                {
                    ll t;
                    //t temp
                    for (t=s[now][1];s[t][0]!=0;t=s[t][0])
                        ;
                    fa[s[t][0]=ans]=t;
                    for (;t!=now;t=fa[t])
                        update(t);
                }
                splay_work(ans);
                return ans;
            }
            
            ll pop(ll x)
            {
                //pop x th number
                ll now,flag;
                for (now=root,flag=1;flag;)
                    if (len[s[now][0]]>=x)
                        now=s[now][0];
                    else
                    {
                        x-=len[s[now][0]];
                        if (x<=sr[now]-sl[now])
                        {
                            if (x!=sr[now]-sl[now])
                                split(now,x);
                            if (x!=1)
                                now=split(now,x-1);
                            flag=0;
                        }
                        else
                        {
                            x-=sr[now]-sl[now];
                            now=s[now][1];
                        }
                    }
                splay_work(now);
                //delete
                fa[s[now][0]]=fa[s[now][1]]=0;
                if (s[now][0]==0)
                    root=s[now][1];
                else
                {
                    ll t;
                    //t temp
                    for (t=s[now][0];s[t][1]!=0;t=s[t][1])
                        ;
                    splay_work(t);
                    update(root=fa[s[t][1]=s[now][1]]=t);
                }
                return sl[now];
            }
            
            ll push_back(ll x)
            {
                //push back number x
                ll ans=insert(x,x+1);
                if (root==0)
                    return (root=ans);
                else
                {
                    ll now;
                    for (now=root;s[now][1]!=0;now=s[now][1])
                        ;
                    splay_work(now);
                    update(fa[s[now][1]=ans]=now);
                    return ans;
                }
            }
        };
        
        splay sp[300000+1];
        
        void main()
        {
            ll n,m,q;
            scanf("%lld%lld%lld",&n,&m,&q);
            size=0;
            for (ll i=1;i<=n;i++)
                sp[i].pr((i-1)*m+1,i*m);
            sp[0].pr(m,m+1);
            for (ll i=2;i<=n;i++)
                sp[0].push_back(i*m);
            for (ll i=1;i<=q;i++)
            {
                ll x,y,temp;
                scanf("%lld%lld",&x,&y);
                sp[x].push_back(sp[0].pop(x));
                printf("%lld\n",temp=sp[x].pop(y));
                sp[0].push_back(temp);
            }
        }
    };
    
    int main()
    {
        dts::main();
    }
    
    • @ 2018-01-26 22:15:18

      用splay做的

  • 1
    @ 2019-07-02 20:02:59

    纯树状数组解法 6500MS过的,最慢的点是1000MS,应该是稳的。

    pre:\ 这最后的20%确实挺有难度的,有不少的细节,如果我是去正式比赛,我应该还是拿前80%稳妥一点...

    然后写完瞅了眼大家的题解,呃,思路应该是差不多的,不过可能有几份题解讲的还不够详细。这里稍微总结一下。

    main:\ 看到题目首先想100%的做法,想10分钟,一点思路都没有,开始看局部分。

    测试点1 - 6 30%

    完全是送分,直接暴力就行了,这个就不详细说了

    测试点7 - 10 20%

    n和m增大,存不下这么多数据了。一开始我没什么思路,先想的后面的数据,不过后面想完,回过头一下就想出来了,为了使得大家更好理解为什么会这样,大家可以先看下面。

    测试点11 - 16 30%

    15和16 2个点就n变大了而已,由于x = 1,所以除了第一行,后面的n全是浪费空间,根本不用管的,所以11 - 16测试点完全可以用同一个算法。

    那么仔细观察这个列队操作,我们发现,会产生变化的只有第一行和最后一列。

    先考虑这个最后一列,把它单独拿出来,对于每次操作,相当于把这个一列的最上面这个数字丢到第一行,然后把删掉的这个数字给插入到这一列的末尾。

    仔细想想,这不就一队列的基本操作么??先进先出,所以直接拿一个队列维护这一列就好了。

    然后考虑怎么维护这一行,有点麻烦,如果我们暴力,查询是O(1),修改是O(n),如果改成链表,反过来,总复杂度还是O(qn),太大了。

    我们猜,应该可以有查询和修改都是log级别的做法。又是线段,然后很自然的想到线段树和树状数组,或者一些查找树之类的。

    一开始我想到的是,拿树状数组维护差分数组搞,搞了一会没法推公式,放弃这个想法了。

    然后刚好想到CF上以前做过的一道题!发现两者是一样的\ CF-978C

    原本的操作(1,4)表示删除第1行第4个数字,我们把它的含义稍微修改一下,操作(1,4)表示,删除第1行第4个还未被删除的数字。

    这样想有什么用呢?我们拿一个数组,一开始都是1,表示没删除,删除以后改成0(这部分是单点修改),然后,前缀和(这部分是区间修改)等于4的点是不是就我们要的答案?而且可以很轻松的想到,这个点有且仅有一个。

    显然这个前缀和是单调不减的,因此直接可以2分找到那个位置!!

    单点修改,区间查询,树状数组基本操作,加上二分,复杂度qlogmlogm!,这部分分到手了!

    然后回过头想7 - 10测试点,显然右边那一列我们得单独拿出来讨论,这在刚才我们已经发现了,因为这一列很特别。

    然后我们发现有个突破口,之前n很大 x = 1,然后很多数字完全不会被访问到,因此省了空间,那这里是不是也可以通过类似的办法省空间呢?

    废话,当然可以,突然发现q太小了,才500,撑死500行会发生修改,你只需要开500 * 50000的数组就完事了。

    你需要离线处理,然后直接离散一下(记得记录行号之间的映射关系),只存存在修改的那几列就行了,然后最后一列单独拿粗来暴力维护。

    整个算法还是暴力的,因此这部分可以跟1 - 6一起做,总复杂度应该是q(n + m),没有压力。

    80分很快就到手了,想加写程序都很轻松,1小时不到就完成了,下面贴一下80分的源码。觉得有困难的可以先练练手(注意开long long,还有强转)

    #include <stdio.h>
    #include <vector>
    #include <algorithm>
    #include <string.h>
    #include <limits.h>
    #include <string>
    #include <iostream>
    #include <queue>
    #include <math.h>
    #include <map>
    #include <stack>
    #include <set>
    #include <complex>
    #define left (now<<1)
    #define right ((now<<1)+1)
    #define mid ((l + r) >> 1)
    #define midmid ((r + mid) >> 1)
    #define LONG_LONG_MIN -9223372036854775808ll
    #define LONG_LONG_MAX 9223372036854775807ll
    using namespace std;
    typedef long long int ll;

    const int MAXN = 3e5 + 10;

    struct s1{
    int x,y;
    };

    int n,m,q,nn;
    queue<ll> qq;
    s1 a[MAXN];
    int bit[2 * MAXN],len;
    ll data[2 * MAXN];
    bool flag;
    ll g[510][50010];
    int pos[50010];

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

    void add(int x,int y){
    while(x <= 600000){
    bit[x] += y; x += lowbit(x);
    }
    }

    int getsum(int x){
    int re = 0;
    while(x > 0){
    re += bit[x]; x -= lowbit(x);
    }
    return re;
    }

    int main(){
    scanf("%d%d%d",&n,&m,&q); flag = true;
    for(int i = 1; i <= q; ++i){
    scanf("%d%d",&a[i].x,&a[i].y);
    if(a[i].x != 1){
    flag = false;
    }
    }
    if(flag){
    for(int i = 2; i <= n; ++i){
    qq.push(1ll * i * m);
    }
    for(int i = 1; i <= 600000; ++i){
    bit[i] = lowbit(i);
    }
    len = m;
    for(int i = 1; i <= m; ++i){
    data[i] = i;
    }
    for(int i = 1; i <= q; ++i){
    int l,r; l = 1; r = len;
    while(l < r){
    if(getsum(mid) >= a[i].y){
    r = mid;
    }else{
    l = mid + 1;
    }
    }
    printf("%lld\n",data[l]); add(l,-1); qq.push(data[l]);
    data[++len] = qq.front(); qq.pop();
    }

    }else{
    if(q <= 500){
    for(int i = 1; i <= n; ++i){
    data[i] = 1ll * m * i;
    pos[i] = 0;
    }
    nn = 0;
    for(int i = 1; i <= q; ++i){
    if(pos[a[i].x] == 0){
    pos[a[i].x] = ++nn;
    for(int j = 1; j <= m - 1; ++j){
    g[nn][j] = 1ll * (a[i].x - 1) * m + j;
    }
    }
    }
    for(int i = 1; i <= q; ++i){
    ll x = a[i].x; ll xx = pos[a[i].x];
    if(a[i].y != m){
    ll ans = g[xx][a[i].y];
    printf("%lld\n",ans);
    for(int j = a[i].y; j < m - 1; ++j){
    g[xx][j] = g[xx][j + 1];
    }
    g[xx][m - 1] = data[x];
    for(int j = x; j < n; ++j){
    data[j] = data[j + 1];
    }
    data[n] = ans;
    }else{
    ll ans = data[x];
    printf("%lld\n",ans);
    for(int j = x; j < n; ++j){
    data[j] = data[j + 1];
    }
    data[n] = ans;
    }
    }
    }else{
    return 0;
    }
    }
    return 0;
    }
    好了,80分到手了,现场的话,1小时T3拿了80还是很舒服的,但是我们现在肯定不满足于只拿80分,剩下20分怎么搞??

    一般前面的数据点都会对正解有提示性,就跟数学考试,前几问一般会对最后一问有提示性作用。

    我们想下刚才整理的思路。

    首先最后一列肯定单独拿出来维护,我们发现,这个时候这个最后一列是删一个点,然后尾部插入,这不就是咱们11 - 16的第一行的算法么??

    直接拿那部分算法的思路维护这一列就OK了,easy

    然后是空间的问题,考虑到我们之前的思路,因为q比起n * m来说,太小太小了,很多点直接仍然是原先的相邻关系,一块一块的。而且之前我们是靠离线处理(预先读入q)来搞一些事,把空间省下来,这里很可能就是类似的办法。

    想到这里还算轻松,后面我就想的有点慢了,因为解法不太清晰。

    由于每次对某行修改,这一行和最后特别的那一列有互动,除此之外跟其他行根本不会有任何影响。因此考虑,把某一行的操作都单独取出来,看看能不能搞点事。

    然后我就立马想到了做法

    假设现在都是对第i行操作,一行有m个,操作按顺序为2,2,4

    按我们刚才的想法,你去删了2,后面2个操作就变成实际删第3个数,和第5个数,这部分也可以拿树状数组搞诶,跟最后一列是一样的。

    那么我们把每一行的操作这样单独取出来,然后算出来它的真实删的第几个数字就好了。

    但是还有个很大的问题,如果算出来真实要删的数字大于等于m怎么办???

    先想等于m的,等于m的时候,其实就是删最后一列去了。这个时候其实跟这一行木有关系,只跟当前这一列的状态有关,那么直接去这一列上搞事就好了,我们就标记一个flag,表示这个是直接在列上搞事,不需要算真实是删第几个数字。

    那么大于m呢(假如是第h个)?显然,删的数字是从那一列第h - (m - 1)次加进来的。

    这里我们发现q很小,也就是说对于所有的行来说,加进来的数字不超过q个(其实就是q个),那我们可以拿一个邻接表(vector就很好~)来存第i行后面加进来的数字。

    那么我们满分的算法就粗来了,我们总结下。

    首先你把所有操作读进来,然后按行排序,同一行按操作先后排序,然后预处理出它真实删除的位置,如果是第m位,flag记一下,不是就算真实位置。

    算完以后按编号重新排序,然后我们开始操作。

    如果这个操作的y小于m,直接可以算出答案,答案就是(x - 1) * m + y 然后去维护那一列,删掉那一列第x大的数字,丢到vector<x>里,把答案插到那一列末尾

    如果这个操作等于m,那么就只在列上搞事就好了,vector也不用丢。

    如果大于m,先减去m(vector是从0开始的),答案就是vector[x][m], 然后去维护最后一列,删掉第x大的数字........这部分不重述了

    然后就AC啦~~~

    这里有个细节,其实这个细节困扰我20,30分钟,我看题解很多人也是用别的办法搞的

    就是你预处理每一行的询问的时候,每次树状数组处理完,你都得初始化,初始化的复杂度最坏是n * 3E5,肯定超时。 如果你开n个树状数组,空间必爆

    所以题解很多人开了动态加点的线段树,呃

    其实这里后来仔细想了想,q不是很少么,每次操作完以后,记一下,你把哪些点单点修改为0了,重新把那些点+1就好了啊、

    这里记不能开bool数组记,不然枚举哪些被改了还是要平方复杂度。

    搞个vector记就好了。

    下面是参考代码:

    #include <stdio.h>
    #include <vector>
    #include <algorithm>
    #include <string.h>
    #include <limits.h>
    #include <string>
    #include <iostream>
    #include <queue>
    #include <math.h>
    #include <map>
    #include <stack>
    #include <set>
    #include <complex>
    #define left (now<<1)
    #define right ((now<<1)+1)
    #define mid ((l + r) >> 1)
    #define midmid ((r + mid) >> 1)
    #define LONG_LONG_MIN -9223372036854775808ll
    #define LONG_LONG_MAX 9223372036854775807ll
    using namespace std;
    typedef long long int ll;

    const int MAXN = 3e5 + 10;

    struct s1{
    int x,y,id;
    bool flag;
    };

    vector<ll> v[MAXN];
    vector<int> a;
    int n,m,q,len,len2,st,ed;
    s1 g[MAXN];
    ll p[2 * MAXN];
    int bit1[2 * MAXN],bit2[2 * MAXN];

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

    void add(int x,int y,int *bit){
    while(x <= 600010){
    bit[x] += y; x += lowbit(x);
    }
    }

    int getsum(int x,int *bit){
    int re = 0;
    while(x > 0){
    re += bit[x]; x -= lowbit(x);
    }
    return re;
    }

    bool cmp1(s1 x,s1 y){
    if(x.x != y.x){
    return x.x < y.x;
    }else{
    return x.id < y.id;
    }
    }

    bool cmp2(s1 x,s1 y){
    return x.id < y.id;
    }

    int main(){
    scanf("%d%d%d",&n,&m,&q); len = n;
    for(int i = 1; i <= q; ++i){
    scanf("%d%d",&g[i].x,&g[i].y);
    g[i].id = i; g[i].flag = false;
    }
    for(int i = 1; i <= n; ++i){
    p[i] = 1ll * i * m;
    }
    for(int i = 1; i <= 600010; ++i){
    bit1[i] = bit2[i] = lowbit(i);
    }
    sort(g + 1,g + 1 + q,cmp1); st = 1; ed = 1;
    while(st <= q){
    while(ed < q && g[st].x == g[ed + 1].x){
    ++ed;
    }
    a.clear(); len2 = m;
    for(int i = st; i <= ed; ++i){
    if(g[i].y == m){
    g[i].flag = true; continue;
    }
    int l = 1,r = len2;
    while(l < r){
    if(getsum(mid,bit2) >= g[i].y){
    r = mid;
    }else{
    l = mid + 1;
    }
    }
    add(l,-1,bit2); a.push_back(l); g[i].y = l; ++len2;
    }
    for(int i = 0; i < a.size(); ++i){
    add(a[i],1,bit2);
    }
    st = ed + 1;
    }
    sort(g + 1,g + 1 + q,cmp2);

    for(int i = 1; i <= q; ++i){
    ll ans;
    if(!g[i].flag){
    if(g[i].y < m){
    ans = 1ll * (g[i].x - 1) * m + g[i].y;
    printf("%lld\n",ans);
    }else{
    g[i].y = g[i].y - m;
    ans = v[g[i].x][g[i].y];
    printf("%lld\n",ans);
    }
    }

    int l = 1,r = len;
    while(l < r){
    if(getsum(mid,bit1) >= g[i].x){
    r = mid;
    }else{
    l = mid + 1;
    }
    }
    add(l,-1,bit1);
    if(!g[i].flag){
    v[g[i].x].push_back(p[l]);
    }else{
    ans = p[l];
    printf("%lld\n",ans);
    }
    p[++len] = ans;
    }
    return 0;
    }

  • 1
    @ 2018-10-30 08:54:26

    原文地址:NOIP2017提高组DAY2题解

    考察知识:树状数组,平衡树,模拟

    算法难度:XXXX+ 实现难度:XXXX+

    分析:

    我们先分析一下列队的移动:

    图片见原文

    如图,我们发现如果红色那块要出队,共需要移动四块。我们发现种序列的移动可以用Splay来维护,仔细观察发现,水平有n行可能需要移动,而竖直只可能发生在最后一列,所以我们维护 n+1 个Splay,维护n个长度为m-1的序列,1个长度为n的序列。

    每次移动时(x,y),我们需要将水平第x个Splay中第y个元素删除,将竖直Splay的第x个元素删除,然后将删除的两个元素分别插入这两个Spaly就可以了。

    但是如果我们直接开Splay,空间不够,注意到,我们可以用Spaly维护区间,每次删除时需要将区间一分为三,然后将中间元素删除并更新剩余两个区间。

    理解了上面的思路后,剩下的就基本是Spaly的基本操作+变形了,考察大家对Splay的熟练程度了。

    注意:

    1.最大值会超过int,所以区间用long long储存

    2.Spaly常数比较大,可能会有部分点超时。(vijos:可以AC;洛谷:不开O2 95 开O2 AC)

    代码:

    #include<cstdio>
    #include<cstdlib>
    #include<cstring>
    #include<algorithm>
    using namespace std;
    #define ll long long
    const int N=300005,maxn=2400005;
    int ch[maxn][2],fa[maxn],sz[maxn],now,cnt[maxn];
    ll L[maxn],R[maxn];
    struct Splay{
        int rt;
        Splay(){rt=0;} 
        int NewNode(ll l,ll r){//新建节点 
            now++,L[now]=l,R[now]=r;
            sz[now]=cnt[now]=r-l+1;
            return now;
        }
        void init(ll l,ll r){rt=NewNode(l,r);}//空树的初始化 
        #define upd(x) cnt[x]=R[x]-L[x]+1
        #define pushup(x) sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+cnt[x]
        #define DB_ for(int i=0;i<=now;i++) \
        printf("[%lld,%lld]%d<- %d[%lld,%lld] ->%d[%lld,%lld]\n", \
        L[ch[i][0]],R[ch[i][0]],ch[i][0],i,L[i],R[i],ch[i][1],L[ch[i][1]],R[ch[i][1]]);\
        puts("----------------")
        int chk(int x){return x==ch[fa[x]][1];}
        void rotate(int x){
            int y=fa[x],z=fa[y],k=chk(x),w=ch[x][k^1];
            ch[y][k]=w,fa[w]=y;
            ch[z][chk(y)]=x,fa[x]=z;
            ch[x][k^1]=y,fa[y]=x;
            pushup(y),pushup(x);
        }
        void splay(int x,int goal=0){
            while(fa[x]!=goal){
                int y=fa[x],z=fa[y];
                if(z!=goal){
                    if(chk(x)==chk(y)) rotate(y);
                    else rotate(x);
                } rotate(x);
            } if(!goal) rt=x;
        }
        void insert(ll l,ll r){//插入到序列末尾 
            if(rt==0) {init(l,r);return;}
            int cur=rt;
            while(ch[cur][1]) cur=ch[cur][1];
            ch[cur][1]=NewNode(l,r),fa[ch[cur][1]]=cur;
            splay(ch[cur][1]);
        }
        int beside(int x,int pre){//求一个子序列的前驱/后继 
            splay(x);
            int cur=ch[x][pre^1];
            if(!cur) return 0;
            while(ch[cur][pre]) cur=ch[cur][pre];
            return cur;
        }
        ll kth_and_del(int k,bool exit_=false){//查找第k大的数并从子序列中删除 
            int cur=rt;
            if(sz[rt]==1){//只有一个数 
                rt=0;//变为空树 
                return L[cur];
            }
            while(true){//kth
                if(ch[cur][0]&&k<=sz[ch[cur][0]])
                  cur=ch[cur][0];
                else if(k>sz[ch[cur][0]]+cnt[cur])
                  k-=sz[ch[cur][0]]+cnt[cur],cur=ch[cur][1];
                else break;
            }
            k-=sz[ch[cur][0]];
            if(exit_) return L[cur]+k-1;//仅查询不删除,用于查看中间结果 
            int pre=beside(cur,1),nxt=beside(cur,0);//del
            if(pre) splay(pre);
            if(nxt) splay(nxt,pre);
            if(L[cur]==R[cur]){//子序列只有一个数,直接删除 
                if(nxt) ch[nxt][0]=0;//特别注意!分类讨论不存在前驱或后继的情况 
                else if(pre) ch[pre][1]=0;
                else rt=0; 
                if(pre) sz[pre]--;
                if(nxt) sz[nxt]--;
                return L[cur];
            }
            else{
                ll ret=L[cur]+k-1;
                if(k==1) L[cur]++,upd(cur),sz[cur]--;
                else if(k==cnt[cur]) R[cur]--,upd(cur),sz[cur]--;
                else{//将子序列一分为三,并删除中间的数 
                    ch[cur][0]=NewNode(L[cur],L[cur]+k-2); 
                    fa[ch[cur][0]]=cur;
                    L[cur]+=k,upd(cur),pushup(cur);
                    splay(cur);
                }
                return ret;
            }
        }
    }s[N];
    int n,m,q;
    void build(){
        scanf("%d%d%d",&n,&m,&q);
        for(int i=1;i<=n;i++)
          s[i].init((ll)(i-1)*m+1,(ll)i*m-1);
        s[0].init((ll)m,(ll)m);
        for(int i=2;i<=n;i++)
          s[0].insert((ll)i*m,(ll)i*m);
    }
    void solve(){
        int x,y;
        ll last=(ll)n*m;
        while(q--){
            scanf("%d%d",&x,&y);
            if(y!=m){
                ll t1=s[x].kth_and_del(y),t2=s[0].kth_and_del(x);
                printf("%lld\n",last=t1);
                s[x].insert(t2,t2);
                s[0].insert(t1,t1);
            } else if(x!=n){
                ll t=s[0].kth_and_del(x);
                printf("%lld\n",last=t);
                s[0].insert(t,t);
            }else{
                printf("%lld\n",last);
            }
    //      DB_; //用于查看当前各Splay节点情况 
        }
    }
    int main(){
        build();
        solve();
        return 0;
    }
    
  • 0
    @ 2021-08-05 09:38:38

    #include <stdio.h>
    #include <vector>
    #include <algorithm>
    #include <string.h>
    #include <limits.h>
    #include <string>
    #include <iostream>
    #include <queue>
    #include <math.h>
    #include <map>
    #include <stack>
    #include <set>
    #include <complex>
    #define left (now<<1)
    #define right ((now<<1)+1)
    #define mid ((l + r) >> 1)
    #define midmid ((r + mid) >> 1)
    #define LONG_LONG_MIN -9223372036854775808ll
    #define LONG_LONG_MAX 9223372036854775807ll
    using namespace std;
    typedef long long int ll;

    const int MAXN = 3e5 + 10;

    struct s1{
    int x,y,id;
    bool flag;
    };

    vector<ll> v[MAXN];
    vector<int> a;
    int n,m,q,len,len2,st,ed;
    s1 g[MAXN];
    ll p[2 * MAXN];
    int bit1[2 * MAXN],bit2[2 * MAXN];

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

    void add(int x,int y,int *bit){
    while(x <= 600010){
    bit[x] += y; x += lowbit(x);
    }
    }

    int getsum(int x,int *bit){
    int re = 0;
    while(x > 0){
    re += bit[x]; x -= lowbit(x);
    }
    return re;
    }

    bool cmp1(s1 x,s1 y){
    if(x.x != y.x){
    return x.x < y.x;
    }else{
    return x.id < y.id;
    }
    }

    bool cmp2(s1 x,s1 y){
    return x.id < y.id;
    }

    int main(){
    scanf("%d%d%d",&n,&m,&q); len = n;
    for(int i = 1; i <= q; ++i){
    scanf("%d%d",&g[i].x,&g[i].y);
    g[i].id = i; g[i].flag = false;
    }
    for(int i = 1; i <= n; ++i){
    p[i] = 1ll * i * m;
    }
    for(int i = 1; i <= 600010; ++i){
    bit1[i] = bit2[i] = lowbit(i);
    }
    sort(g + 1,g + 1 + q,cmp1); st = 1; ed = 1;
    while(st <= q){
    while(ed < q && g[st].x == g[ed + 1].x){
    ++ed;
    }
    a.clear(); len2 = m;
    for(int i = st; i <= ed; ++i){
    if(g[i].y == m){
    g[i].flag = true; continue;
    }
    int l = 1,r = len2;
    while(l < r){
    if(getsum(mid,bit2) >= g[i].y){
    r = mid;
    }else{
    l = mid + 1;
    }
    }
    add(l,-1,bit2); a.push_back(l); g[i].y = l; ++len2;
    }
    for(int i = 0; i < a.size(); ++i){
    add(a[i],1,bit2);
    }
    st = ed + 1;
    }
    sort(g + 1,g + 1 + q,cmp2);

    for(int i = 1; i <= q; ++i){
    ll ans;
    if(!g[i].flag){
    if(g[i].y < m){
    ans = 1ll * (g[i].x - 1) * m + g[i].y;
    printf("%lld\n",ans);
    }else{
    g[i].y = g[i].y - m;
    ans = v[g[i].x][g[i].y];
    printf("%lld\n",ans);
    }
    }

    int l = 1,r = len;
    while(l < r){
    if(getsum(mid,bit1) >= g[i].x){
    r = mid;
    }else{
    l = mid + 1;
    }
    }
    add(l,-1,bit1);
    if(!g[i].flag){
    v[g[i].x].push_back(p[l]);
    }else{
    ans = p[l];
    printf("%lld\n",ans);
    }
    p[++len] = ans;
    }
    return 0;
    }

  • 1

信息

ID
2033
难度
6
分类
(无)
标签
递交数
249
已通过
60
通过率
24%
被复制
9
上传者