P1198 [JSOI2008]最大数
题目传送门
多姿多彩的做法
暴力(非正解)
暴力还是很好打的。整体时间复杂度为 \(O(ml)\) (极端情况,\(l\) 为数列长度)。
线段树
考试的时候本来想这么写的,可是写挂了,我太弱了。
由于初始集合是 \(0\) ,我们不需要写性能瓶颈(时间复杂度 \(O(m\log m)\))的建树,只需要写维护最大值(记得取模)的单点修改与区间查询就成。
整体时间复杂度为 \(O(m \log m)\)。
#include
#define int long long
using namespace std;
int t[800005];
int m,d;
char op;
int x,tt,cnt;
void pushup(int i){
t[i]=max(t[i<<1],t[i<<1|1])%d;
}
void update(int pv,int v,int i,int l,int r){
if(l==r){
t[i]=v;
return;
}
int mid=(l+r)>>1;
if(mid>=pv){
update(pv,v,i<<1,l,mid);
}
if(mid=r){
return t[i];
}
int mid=(l+r)>>1;
int result=LLONG_MIN;
if(ql<=mid){
result=max(result,ask(ql,qr,i<<1,l,mid));
}
if(qr>mid){
result=max(result,ask(ql,qr,i<<1|1,mid+1,r));
}
return result;
}
signed main(){
cin>>m>>d;
for(int i=1;i<=m;i++){
cin>>op>>x;
if(op=='A'){
update(cnt+1,(x+tt)%d,1,1,m);
cnt++;
}
else{
if(x==0)tt=0;
else tt=ask(cnt-x+1,cnt,1,1,m)%d;
cout<
Record
单调栈+单调栈上二分
由于只查询最后几个元素,因此只要建两个单调栈,一个维护值,一个维护位置。然后二分找下标。
整体时间复杂度为 \(O(m \log m)\)。
#include
#define int long long
using namespace std;
int m,t,d;
int sta[3][200005],top,cnt;
void push(int k){
cnt++;
while(k>sta[1][top]&&top>0){
top--; // Pop if it's smaller than the element it will be push
}
sta[0][++top]=cnt;
sta[1][top]=k;
}
bool check(int x,int v){
return sta[0][x]>1;
if(check(mid,v)){
l=mid+1;
}
else{
r=mid;
}
}
t=sta[1][r];
return t;
}
signed main(){
cin>>m>>d;
for(int i=1;i<=m;i++){
char op;int x;
cin>>op>>x;
if(op=='A'){
push((t+x)%d);
}
else{
cout<
Record
ST表
参考自这里
这里的ST表 \(f[i][j]\) 维护的是区间 \(a[i,i-(2^j)+1]\) 的最值。
ST表可以维护RMQ问题,本问题可以用ST表解答,插入不会影响原来的ST表。
时间复杂度还是熟悉的 \(O(m \log m)\)。
#include
#define int long long
using namespace std;
int a[200005],f[200005][25],t,d,n,m;
void change(int u){
f[u][0]=a[u];
for(int i=1;u-(1<=0;i++){
f[u][i]=max(f[u][i-1],f[u-(1<<(i-1))][i-1]);
}
}
int find(int x,int y){
double t=log(y-x+1)/log(2);
int K=t;
return max(f[y][K],f[x+(1<>m>>d;
for(int i=1;i<=m;i++){
char c;int x;
cin>>c>>x;
if(c=='A'){
a[++n]=(x+t)%d; // it's simliar than BF algorithm
change(n);
}
else{
int ans;
if(ans==1){
cout<
Record
单调队列+单调队上二分
考虑维护一个单调队列,维护以最后一个数结束的极大值。
插入数字=入队
接着二分查询执行查询操作。
整体时间复杂度为 \(O(m \log m)\)。
这里写的是std::lower_bound
(不想粘贴二分模板)。
#include
#define int long long
using namespace std;
int a[200005],q[200005];
int m,d,head,tail,t;
signed main(){
cin>>m>>d;
for(int i=1;i<=m;i++){
char op;int x;
cin>>op>>x;
if(op=='A'){
a[++head]=(x+t)%d;
while(tail>0&&a[q[tail-1]]
Record
单调队列+并查集维护下标
其实只要维护一个集合(可以用并查集实现),存储下标,找根即可。
出队时合并集合。
使用路径压缩优化并查集,整体时间复杂度 \(O(m \log m)\),一般为 \(O(m\alpha(m))=O(m)\)。可以说是线性的。
注意:不能按秩合并,会全WA的!
#include
//#define int long long
using namespace std;
int a[200005],q[200005];
int m,d,head,tail,t;
int fa[200005];
//int Rank[200005];
int find(int x){
if(fa[x]==x)return x;
else return fa[x]=find(fa[x]);
}
int merge(int x,int y){
int a=find(x),b=find(y);
fa[a]=b;
}
signed main(){
srand(time(0));
cin>>m>>d;
for(int i=1;i<=m;i++){
char op;int x;
cin>>op>>x;
if(op=='A'){
a[++head]=(x+t)%d;
fa[head]=head;
//Rank[head]=rand();
while(tail>0&&a[q[tail-1]]
Record