NOIP2017 列队 线段树(指针版)+vector

题目描述
题目

考试有想过每行建一棵树,但是发现空间会爆,就去打模拟了。。。
然而,正解就是每行建一棵树(最后一列单独建一棵),只不过是把插入操作改成删除操作就好了。
因为每次离队影响到的只会是人所在的行和列(如果是最后一列的离队只会影响行)。
对于后面插进来的元素用vector存一下就好

懒得去算要开多大的空间,并且最近玩数据结构玩得有点疯,就打了个指针版的线段树(并没有和打平衡树一样查错查半天,有点开心)

下面是代码

#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<algorithm>
#include<vector>
#define ll long long
using namespace std;
const int N=300010;
ll n,m,q,mn;

struct node
{
    node* ch[2];ll s;
    node(){ch[0]=ch[1]=NULL;s=1;}
    void maintain()
    { 
        s=0; 
        if(ch[0] != NULL) s+=ch[0]->s; 
        if(ch[1] != NULL) s+=ch[1]->s; 
    }
};
node* rt[N];
vector<ll> g[N];

ll query(node* o,ll l,ll r,ll x)
{
    if(o == NULL) return l+x-1;
    if(l == r) return l;
    int mid=(l+r)>>1,ss=(o->ch[0]==NULL?0:o->ch[0]->s);   
    int h=mid-l+1-ss;   
    if(h >= x) return query(o->ch[0],l,mid,x);
    return query(o->ch[1],mid+1,r,x-h);
}

void del(node* &o,ll l,ll r,ll x)
{
    if(o == NULL) o=new node();
    if(l == r) return ;
    int mid=(l+r)>>1;
    if(x <= mid) del(o->ch[0],l,mid,x);
    else del(o->ch[1],mid+1,r,x);
    o->maintain();
}

ll del_row(ll x)
{
    ll idx=query(rt[n+1],1,mn,x);
    del(rt[n+1],1,mn,idx);
    if(idx > n) return g[n+1][idx-n-1];
    return idx*m;
}

ll del_line(ll x,ll y)
{
    ll idx=query(rt[x],1,mn,y);
    del(rt[x],1,mn,idx);
    if(idx >= m) return g[x][idx-m];
    return (x-1)*m+idx;
}

int read()
{
    int out=0,f=1;char c=getchar();
    while(c < '0' || c > '9') {if(c == '-') f=-1;c=getchar();}
    while(c >= '0' && c <= '9' ) 
    {out=(out<<1)+(out<<3)+c-'0';c=getchar();}
    return out*f;
}

void solve()
{
    n=read();m=read();q=read();mn=max(n,m)+q;
    for(int i=1;i<=n+1;i++) rt[i]=NULL;
    for(int i=1;i<=q;i++)
    {
        ll x=read(),y=read();
        if(y == m)
        {
            ll id=del_row(x);
            g[n+1].push_back(id);
            printf("%lld\n",id);
        }
        else
        {
            ll id1=del_line(x,y),id2=del_row(x);
            g[x].push_back(id2);g[n+1].push_back(id1);
            printf("%lld\n",id1);
        }
    }
}

int main()
{
    solve();
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值