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

题目定义了一个特殊列,该列上的点是特殊点。
对于图中的每个点,其实可以按照 ( y + a b s ( x − k ) ) (y+abs(x-k)) (y+abs(xk))分类,这个值代表这个点可以移动到特殊点时的最小行。
然后问题模型就变成了一个一维平面上的模型。
每一特殊点对应的行都可能有0或多个要放入的点,因为一行只能放一个点,多余的要往后放。
要求的是最远的那个点。
首先考虑到最远的点一定是由行数最大的特殊点或者由它溢出产生的。
但是前面那些行数的特殊点也有溢出点后移,可能会溢出到行数最大的特殊点。

所以要怎么做呢?

我首先维护了一个优先队列,表明各个放入点对应特殊点的最小行。
因为答案是在最大行产生的,这个优先队列就是用来维护这个最大行的。
cnt数组是用来记录该行的特殊点被对应次数,当队列的top的cnt为0时,显然要弹出这个top.

因为每个点已放入的话再出现就要删除,我这里用了个vis记录是否出现。

然后关键就是线段树的维护了.
我把线段树的初始点值从右(400000)到左从0开始依次递减。即第i个点初值为400000-i.
(如果行数小的特殊点要溢出到行数大的位置,显然每次移动他是要失去一个的)

update(1, 1, pos, 1);

更新时往前更新,可以结合下面的查询和上面的初始化用样例自己体验一下整个过程。
(pos为top())

update(1,1,pos,-cnt[pos]);
long long ans=max(0ll,quiry(1,1,pos-1)+(400000-pos))+cnt[pos]+pos-1-n;
ans=max(ans,0ll);
update(1,1,pos,cnt[pos]);

查找是这样的,找到这个点之前的点的最大值+(400000-pos)就是它可以影响到当前最大行数的值。
这样如果出现了一个大于0的值,就表明可以对结果产生影响,加上即可。
后面的cnt[pos]+pos-1-n是不考虑前面影响的最大行数对应的答案,这个答案加上之前查询到的最大值即可。

#include<bits/stdc++.h>
using namespace std;
#define ll long long
#define MAXN 400005
#define rep(n) for(int i=1;i<=n;i++)
#define rall(x) for(int i=(x).size()-1;i>=0;i--)
#define all(x) for(int i=0;i<(x).size();i++)
int n,k,m;
#define ls (root*2)
#define rs (root*2+1)
#define tmid ((tree[root].l+tree[root].r)/2)
struct tree{
    int l;
    int r;
    ll val;
    ll lazy;
}tree[4*MAXN];
void build(int root,int l,int r)
{
    tree[root].l=l;
    tree[root].r=r;
    if(l==r)
    {
        tree[root].val=l-400000;
        tree[root].lazy=0;
        return ;
    }
    int mid=(l+r)/2;
    build(ls,l,mid);
    build(rs,mid+1,r);
    tree[root].val=max(tree[ls].val,tree[rs].val);
}
void pushdown(int root)
{
    tree[ls].lazy+=tree[root].lazy;
    tree[rs].lazy+=tree[root].lazy;
    tree[ls].val+=tree[root].lazy;
    tree[rs].val+=tree[root].lazy;
    tree[root].lazy=0;
}
void update(int root,int l,int r,int val)
{
    if(l==tree[root].l&&r==tree[root].r)
    {
        tree[root].lazy+=val;
        tree[root].val+=val;
        return ;
    }
    pushdown(root);
    if(l<=tmid&&r>tmid)
    {
        update(ls,l,tmid,val);
        update(rs,tmid+1,r,val);
    }
    else if(r<=tmid)
        update(ls,l,r,val);
    else if(l>tmid)
        update(rs,l,r,val);
    tree[root].val=max(tree[ls].val,tree[rs].val);
}
ll quiry(int root,int l,int r)
{
    if(l==tree[root].l&&r==tree[root].r)
        return tree[root].val;
    pushdown(root);
    if(l<=tmid&&r>tmid)
        return max(quiry(ls,l,tmid),quiry(rs,tmid+1,r));
    else if(l>tmid)
        return quiry(rs,l,r);
    else if(r<=tmid)
        return quiry(ls,l,r);
}
int cnt[MAXN];
unordered_map<ll,int>vis;
ll id(ll x,ll y)
{
    return 1ll*x*n+y;
}
int main()
{
    scanf("%d%d%d",&n,&k,&m);
    build(1,1,400000);
    priority_queue<int>q;
    for(int i=1;i<=m;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        int pos=y+abs(x-k);

        if(!vis.count(id(x,y))) {
            vis[id(x,y)]=1;
            update(1, 1, pos, 1);
            q.push(pos);
            cnt[pos]++;
        }
        else
        {
            vis.erase(id(x,y));
            update(1,1,pos,-1);
            cnt[pos]--;
            while(q.size()&&cnt[q.top()]==0)
                q.pop();
        }
        if(q.empty())
        {
            cout<<"0"<<endl;
            continue;
        }
        pos=q.top();
        if(pos==1)
        {
            cout<<max(0,cnt[pos]-n)<<endl;
            continue;
        }
        update(1,1,pos,-cnt[pos]);
        long long ans=max(0ll,quiry(1,1,pos-1)+(400000-pos))+cnt[pos]+pos-1-n;
        ans=max(ans,0ll);
        update(1,1,pos,cnt[pos]);
        cout<<ans<<endl;
    }

}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值