洛谷P3960 列队

题目

题解:

我们发现其实就是维护n+1个序列,支持查找和删除第x个元素,以及在最后添加元素
前n个序列维护每一行的前m-1个元素,最后一个序列维护最后一列的元素
但是这样的话需要建n+1颗线段树,无法承受
但是可以发现一开始线段树中的元素是满的,并且一开始的元素编号十分有规律,可以直接计算
那么我们一开始就直接把所有线段树当作满的,用动态开点每次删点,后面加进来的点用vector储存,
当删除(x,y)时
如果y =m那么只要操作最后一列,如果是原来最后一列的元素,就直接计算,否则从vector中提取
如果y!=m那么先对第x行进行一次操作,转化成删除(x,m)位置的元素,然后再对最后一列进行操作
因为每次操作最多会加入两个元素,所以最多只会有O(2q)的元素储存在vector中
时间复杂度O(nlogn),空间复杂度O(nlogn)

标程:

//sz[i]表示i的子树中空格(已经被取掉的格子)的个数
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=300003,M=10000003;
int n,m,q,x,y,sz[M],cnt,le[M],ri[M],mx,rt[N];
vector<ll>G[N];
ll id;
int read(){
    char c;int x=0,f=1;
    do{c=getchar();if(c=='-')f=-1;}while(c<48||c>57);
    do x=(x<<1)+(x<<3)+(c^48),c=getchar();while(c>=48&&c<=57);
    return f*x;
}
int query(int l,int r,int &p,int x){
    if (l==r) return l;
    int mid=l+r>>1,h=mid-l+1-sz[le[p]];
    if (x<=h) return query(l,mid,le[p],x);
    return query(mid+1,r,ri[p],x-h);
}
void del(int l,int r,int &p,int x){
    if (!p) p=++cnt;
    sz[p]++;
    if (l==r) return;
    int mid=l+r>>1;
    if (x<=mid) del(l,mid,le[p],x);
    else del(mid+1,r,ri[p],x);
}
ll del_line(int x,int y){
    int pos=query(1,mx,rt[x],y);
    del(1,mx,rt[x],pos);
    return pos<m?1ll*(x-1)*m+pos:G[x][pos-m];
}
ll del_row(int x){
    int pos=query(1,mx,rt[n+1],x);
    del(1,mx,rt[n+1],pos);
    return pos<=n?1ll*(pos-1)*m+m:G[n+1][pos-n-1];
}
int main(){
    n=read();m=read();q=read();
    mx=max(n,m)+q;
    while (q--){
        x=read();y=read();
        if (y==m){
            id=del_row(x);
            G[n+1].push_back(id);
            printf("%lld\n",id);
        }else{
            id=del_line(x,y);
            G[n+1].push_back(id);
            printf("%lld\n",id);
            id=del_row(x);
            G[x].push_back(id);
        }
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值