[树状数组]Logistyka

题目描述

维护一个长度为n的序列,一开始都是0,支持以下两种操作:
1.U k a 将序列中第k个数修改为a。
2.Z c s 在这个序列上,每次选出c个正数,并将它们都减去1,询问能否进行s次操作。
每次询问独立,即每次询问不会对序列进行修改。

输入

第一行包含两个正整数n,m(1<=n,m<=1000000),分别表示序列长度和操作次数。
接下来m行为m个操作,其中1<=k,c<=n,0<=a<=10^9,1<=s<=10^9。

输出

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

样例输入

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

样例输出

NIE
TAK
NIE
TAK

思路:对于第二个询问,如果序列中大于等于s的数有cnt个,那么当小于s的数之和sum>=(c-cnt)*s时,可行;否则不可行;
用两个权值树状数组分别维护序列中小于s的数的个数及小于s的数之和
AC代码:
#include <iostream>
#include<cstdio>
#include<algorithm>
typedef long long ll;
using namespace std;

ll a[1000005];
struct Query{char kind;ll x,y;}q[1000005];
ll num[1000005],tot=0;
inline ll getid(ll x){
  return lower_bound(num+1,num+1+tot,x)-num;
}
ll tr_b[1000005],tr_c[1000005];

inline void add(ll pos,ll x,ll y){
 for(ll i=pos;i<=tot;i+=i&(-i)) tr_b[i]+=x,tr_c[i]+=y;
}


inline ll getsum_b(ll pos){
  ll ret=0;
  for(ll i=pos;i>0;i-=i&(-i)) ret+=tr_b[i];
  return ret;
}

inline ll getsum_c(ll pos){
  ll ret=0;
  for(ll i=pos;i>0;i-=i&(-i)) ret+=tr_c[i];
  return ret;
}

int main()
{
    ll n,m;scanf("%lld%lld",&n,&m);
    for(ll i=1;i<=m;i++){
        scanf(" %c%lld%lld",&q[i].kind,&q[i].x,&q[i].y);
        num[++tot]=q[i].y;
    }
    sort(num+1,num+1+tot);
    tot=unique(num+1,num+1+tot)-(num+1);
    for(ll i=1;i<=m;i++){
        if(q[i].kind=='U'){
            ll pos=q[i].x,x=q[i].y;
            if(a[pos]){
                add(getid(a[pos]),-1,-a[pos]);
            }
            a[pos]=x;
            add(getid(a[pos]),1,a[pos]);
        }
        else{
            ll c=q[i].x,s=q[i].y;
            ll cnt=getsum_b(tot)-getsum_b(getid(s)-1);
            ll sum=getsum_c(getid(s)-1);
            if(sum>=(c-cnt)*s) puts("TAK");
            else puts("NIE");
        }
    }
    return 0;
}
 
转载请注明出处:https://www.cnblogs.com/lllxq/
原文地址:https://www.cnblogs.com/lllxq/p/10115613.html