Educational Codeforces Round 90 (Rated for Div. 2) G. Pawns (线段树)

G. Pawns

time limit per test

3 seconds

memory limit per test

256 megabytes

input

standard input

output

standard output

You are given a chessboard consisting of nn rows and nn columns. Rows are numbered from bottom to top from 11 to nn. Columns are numbered from left to right from 11 to nn. The cell at the intersection of the xx-th column and the yy-th row is denoted as (x,y)(x,y). Furthermore, the kk-th column is a special column.

Initially, the board is empty. There are mm changes to the board. During the ii-th change one pawn is added or removed from the board. The current board is good if we can move all pawns to the special column by the followings rules:

  • Pawn in the cell (x,y)(x,y) can be moved to the cell (x,y+1)(x,y+1), (x−1,y+1)(x−1,y+1) or (x+1,y+1)(x+1,y+1);
  • You can make as many such moves as you like;
  • Pawns can not be moved outside the chessboard;
  • Each cell can not contain more than one pawn.

The current board may not always be good. To fix it, you can add new rows to the board. New rows are added at the top, i. e. they will have numbers n+1,n+2,n+3,…n+1,n+2,n+3,….

After each of mm changes, print one integer — the minimum number of rows which you have to add to make the board good.

Input

The first line contains three integers nn, kk and mm (1≤n,m≤2⋅105;1≤k≤n1≤n,m≤2⋅105;1≤k≤n) — the size of the board, the index of the special column and the number of changes respectively.

Then mm lines follow. The ii-th line contains two integers xx and yy (1≤x,y≤n1≤x,y≤n) — the index of the column and the index of the row respectively. If there is no pawn in the cell (x,y)(x,y), then you add a pawn to this cell, otherwise — you remove the pawn from this cell.

Output

After each change print one integer — the minimum number of rows which you have to add to make the board good.

Example

input

Copy

5 3 5
4 4
3 5
2 4
3 4
3 5

output

Copy

0
1
2
2
1

题意:给你一个n*n(<=2e5)的棋盘,其中第k(<=n)列是特殊的,进行m(<=2e5)次操作。

每次操作会给出一个坐标(x,y)表示第x列 第y行,若该点没有棋子则放一个,否则将该点的棋子拿走。

每次操作之后,你要在脑中想,如果每个棋子(x,y)只能移动到 (x-1,y+1)、(x,y+1)、 (x+1,y+1),移动次数不限制,那么至少需要增加多少行,才能使得所有棋子都可以移动到第k列?每次操作之后输出一个数表示答案。

分析:感觉好难想到啊。因为棋子每移动一步,行数必定+1,所以我们可以计算出该棋子移动到第k列时最小的行数pos,这个棋子可以移动到第k列中任何行数>=pos的位置。

如果只是一次查询,那么我们只需要先记录所有的pos上棋子的数量,最后从第k列第一行往下扫,直到所有的棋子都获得一个不同的行标。但是由于是m次查询,因此我们就需要维护一些东西。

最巧妙的地方:对于第k列某行pos,如果它前面的行足够摆下pos-1行之前的棋子,那么pos位置的棋子数量就决定了pos这个位置需要往下增加多少行,最大的pos的棋子数量也就确定了最后的答案。如果它前面的行不足以摆下pos-1行之前的棋子,那么实际上第pos的位置上的棋子个数(对答案的影响)相当于放到第pos-1的位置了。

因此,初始化下标i=[1,n+n]的值为i-1(注意pos最大为2*n-1) 对于每颗棋子,对应第k列位置为pos,我们就把下标[1,pos]对应的值都+1,这样每一个下标的值都表示从这一行开始往下摆放所有后面的棋子的最终行数了。用线段树维护一下,取个最大值就可以了。注意0时的特殊处理。注意数组大小。

代码:

#include<bits/stdc++.h>
#define ll long long
#define inf 0x3f3f3f3f
#define mst(head,x,n) memset(head+1,x,n*sizeof(head[0]))
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dep(i,a,b) for(int i=(a);i>=(b);i--)
#define lson rt<<1
#define rson rt<<1|1
using namespace std;
const int maxn=4e5+5;
//const double pi=acos(-1.0);
//const double eps=1e-9;
//const ll mo=1e9+7;
int n,m,k;
int a[maxn<<2],c[maxn<<2],num[maxn<<2];
int ans,tmp,cnt;
int flag;
template <typename T>
inline void read(T &X){
    X=0;int w=0; char ch=0;
    while(!isdigit(ch)) {w|=ch=='-';ch=getchar();}
    while(isdigit(ch)) X=(X<<3)+(X<<1)+(ch^48),ch=getchar();
    if(w) X=-X;
}
void pu(int rt){
    c[rt]=max(c[lson],c[rson]);
}
void pd(int rt){
    if(a[rt]){
        c[lson]+=a[rt];
        c[rson]+=a[rt];
        a[lson]+=a[rt];
        a[rson]+=a[rt];
        a[rt]=0;
    }
}
void build(int rt,int l,int r){
    if(l==r){
        c[rt]=l-1;
        a[rt]=0;
        return ;
    }
    int mid=(l+r)>>1;
    build(lson,l,mid);
    build(rson,mid+1,r);
    pu(rt);
}
void update(int rt,int l,int r,int L,int R,int v){
    if(L<=l&&r<=R){
        a[rt]+=v;
        c[rt]+=v;
        return ;
    }
    pd(rt);
    int mid=(l+r)>>1;
    if(L<=mid) update(lson,l,mid,L,R,v);
    if(R>mid) update(rson,mid+1,r,L,R,v);
    pu(rt);
}
int query(int rt,int l,int r,int L,int R){
    if(L<=l&&r<=R){
        return c[rt];
    }
    pd(rt);
    int mid=(l+r)>>1;
    int ans=0;
    if(L<=mid) ans=max(ans,query(lson,l,mid,L,R));
    if(R>mid) ans=max(ans,query(rson,mid+1,r,L,R));
    //pu(rt);
    return ans;
}
int main(){

#ifdef ONLINE_JUDGE
#else
    freopen("D:/Temp/in.txt", "r", stdin);
#endif

    int T,cas=1;
    //read(T);
    //while(T--)
    {
        read(n);read(k);read(m);
        set<pair<int,int> >st;
        set<int>lst;
        build(1,1,n+n);
        rep(i,1,m){
            int x,y;
            read(x);read(y);
            int pos=y+abs(x-k);
            pair<int,int>p=make_pair(x,y);
            if(st.count(p)){
                num[pos]--;
                if(num[pos]==0) lst.erase(pos);
                st.erase(p);
                update(1,1,n+n,1,pos,-1);
            }
            else {
                st.insert(p);
                num[pos]++;
                if(num[pos]==1) lst.insert(pos);
                update(1,1,n+n,1,pos,1);
            }
            //cout<<"debug: "<<i<<" "<<x<<" "<<y<<" "<<pos<<" "<<num[pos]<<" "<<(*lst.rbegin())<<endl;
            if(lst.empty()) puts("0");
            else printf("%d\n",max(0,query(1,1,n+n,1,*(lst.rbegin()))-n));
        }
    }
    return 0;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值