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 1Sample 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 所以不会出现一个抽屉多次放置的状况。于是有以下推论式:若满足
观察上述式子,我们需要知道小于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");
}
}
}