标签:表示 har 答案 记录 update font bzoj 整数 lang
Descrption
现在请求你维护一个数列,要求提供以下两种操作:
注意:初始时数列是空的,没有一个数。
Input
第一行两个整数,\(M\)和\(D\),其中\(M\)表示操作的个数\((M <= 200,000)\),\(D\)如上文中所述,满足\((0<D<2,000,000,000)\)
接下来的\(M\)行,每行一个字符串,描述一个具体的操作。语法如上文所述。
Output
Sample Input
5 100
A 96
Q 1
A 97
Q 1
Q 2
Sample Output
96
93
96
Hint
分析:
方法一:单点修改,区间查询,显然是线段树该干的事嘛,先略。
方法二:单调栈和二分。
因为求的是后 \(L\) 数的最值,所以当当前添加的数比前面的数大,实际上前面的数就没有必要再存在了。所以我们就可以维护一个单调递减的一个栈。
显然,栈最底部是 \([1,n]\) 的最大值,那如何求 \([L,n]\)的最大值呢?我们以同步用一个数组记录当前在栈里的没有个数对应的\(id\) ,只需找到 \(id\) 数组中第一个大于或等于 \(n-L+1\)的位置,然后单调栈的当前位置的值即为答案,因为\(id\) 数组是单调递增的,二分查找即可。
Code
#include <bits/stdc++.h>
const int maxn=2e5+5;
int n,tail,head,m,mod;
int q[maxn],id[maxn];
void add(int x){//后加的数比前面的大,前面的就没什么用了
while(q[tail]<=x&&tail)tail--;
q[++tail]=x;id[tail]=++n;//q和id是同步的,只是记录的结果不一样
}
int qurey(int x){//查询
int l=n-x+1;//手动模拟下不太好讲
int k=std::lower_bound(id+head,id+tail+1,l)-id;
return q[k];
}
void Solve(){
scanf("%d%d",&m,&mod);
int last=0;
head=1;tail=0;
while(m--){
char str[2];
int a;
scanf("%s%d",str,&a);
if(str[0]==‘A‘)
add((a+last)%mod);
else
printf("%d\n",last=qurey(a));
}
}
int main(){
Solve();
return 0;
}
方法三:树状数组
树状数组大家熟悉的是单点修改,区间求和,其实树状数组也可以维护前缀或后缀的最值,前缀最值因为当前修改影响的是当前和后面的结果,所以我们一般用向上更新,向下求值,后缀最值刚好相反,一般是向下更新,向上求值。具体见代码。
Code
#include <bits/stdc++.h>
const int maxn = 2e5+10;
int m,cnt;
int D,last,c[maxn];
int lowbit(int x) {return x&(-x);}
void update(int i,int value){//向下更新
for(;i;i-=lowbit(i))
c[i]=std::max(c[i],value);
}
int query(int i){//向上查询
int res = 0;
for(;i<=m;i+=lowbit(i))//m次操作最多有m个数,m之上就不用查询了
res=std::max(res,c[i]);
return res;
}
void Solve(){
scanf("%d%d",&m,&D);
for(int i=1;i<=m;i++){
char s[2];int x;
scanf("%s%d",s,&x);
if(s[0]==‘A‘){
cnt++;
update(cnt,(x+last)%D);
}
else{
last = query(cnt-x+1);
printf("%lld\n",last);
}
}
}
int main(){
Solve();
return 0;
}
标签:表示 har 答案 记录 update font bzoj 整数 lang
原文地址:https://www.cnblogs.com/hbhszxyb/p/13194239.html