BZOJ4378 POI2015 Logistyka


Description

维护一个长度为n的序列,一开始都是0,支持以下两种操作:

  • U k a 将序列中第k个数修改为a。
  • Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作。

每次询问独立,即每次询问不会对序列进行修改。

Input

第一行包含两个正整数n,m(1<=n,m<=1000000),分别表示序列长度和操作次数。

接下来m行为m个操作,其中1<=k,c<=n,0<=a<=10^9,1<=s<=10^9。

Output

包含若干行,对于每个Z询问,若可行,输出TAK,否则输出NIE。

Sample Input

3 8
U 1 5
U 2 7
Z 2 6
U 3 1
Z 2 6
U 2 2
Z 2 6
Z 2 1

Sample Output

NIE
TAK
NIE
TAK

Solution:

这道数据结构题的关键操作在于第二条:“选出c个整数并分别减一,询问能否进行s次操作”。

一开始我考虑了“求最多操作次数”的贪心,但是优先取前c个最小值和优先取前c个最大值的算法都是有问题的,后一个贪心被“3,3,3”一类的数据给卡掉了。如果不一次性更新多个,总复杂度就达到了 O(s×c×logn) 级别。显然考虑该变种题对本题没有借鉴意义。

上文亦需要一个非常显然的转化,就是将s次操作转化为权值s。接下来就略玄学了,我们根据s次操作一定能发现,如果有权值>=s的 ai ,那么对于s次操作中,我们在选定的c长度的序列中,一定可以让这个 ai 始终占据一个位置,而不会比不加不优。那么接下来只会考虑剩下p个位置如何填入。

对于其他剩下的元素,由于只能在每一次操作中出现一次,所以只能将这些点拆成 ai 个1放入s个“抽屉”中。由于 ai<s 所以不会出现一个抽屉多次放置的状况。于是有以下推论式:若满足

i1n{aiai<s}>=ps
则可以完成这个询问。

观察上述式子,我们需要知道小于s的所有权值的sum和,以及所有不小于s的权值个数,用一般的数据结构都可以优化。

#include <bits/stdc++.h>
#define M 1000005
using namespace std;
template <class temp>
inline void Rd(temp &res){
    res=0;char c;
    while(c=getchar(),c<48);
    do res=(res<<3)+(res<<1)+(c^48);
    while(c=getchar(),c>47);
}
struct Node{
    int a,b;
    char c;
}Q[M];
int n,m,A[M],res[M],rtop=0;

/*Binary Indexed Tree*/
int cnt[M];
long long sum[M];
#define lowbit(x) x&(-x)
template <class temp>
void update(temp *bit,int pos,int val){
    if(!val)return;
    while(pos<=rtop){
        bit[pos]+=val;
        pos+=lowbit(pos);
    }
}
template <class temp> 
temp query(temp *bit,int pos){
    temp ans=0;
    while(pos){
        ans+=bit[pos];
        pos-=lowbit(pos);
    }
    return ans;
}

int main(){
    char str[5]={0};
    Rd(n),Rd(m);
    res[++rtop]=0;
    for(int i=1,a,b;i<=m;i++){
        scanf("%s",str);
        Rd(a),Rd(b);
        Q[i]=(Node){a,b,str[0]};
        res[++rtop]=b;
    }
    sort(res+1,res+rtop+1);
    rtop=unique(res+1,res+rtop+1)-(res+1);

    update(cnt,1,n);
    for(int i=1;i<=n;i++)A[i]=1;

    for(int i=1;i<=m;i++){
        Q[i].b=lower_bound(res+1,res+rtop+1,Q[i].b)-res;
        int &pos_pre=A[Q[i].a],pos_now=Q[i].b;
        if(Q[i].c=='U'){//原序列Q[i].a位置上的数修改为Q[i].b
            int val_pre=res[pos_pre],val_now=res[pos_now];

            update(cnt,pos_pre,-1);
            update(cnt,pos_now,1);
            update(sum,pos_pre,-val_pre);
            update(sum,pos_now,val_now);

            pos_pre=pos_now;
        }else if(Q[i].c=='Z'){//每次选出Q[i].a个正数,进行Q[i].b次操作
            int c=Q[i].a;
            int p=n-query(cnt,pos_now-1);
            if(p>=c){
                puts("TAK");
                continue;
            }
            p=c-p;
            if(query(sum,pos_now-1)>=1ll*p*res[pos_now])puts("TAK");
            else puts("NIE");
        }
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值