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