题解 P1198 【[JSOI2008]最大数】
扫一眼题面:这不是一道线段树板子题吗?
然而数据过水导致暴力n方能过? 请求管理加强数据QAQ
所以我们还是要用正解来做这一道题啦(线段树求区间最大值)!
主要内容是单点修改和区间查询,尤其要主要L==0的情况,不然会RE
这是用线段树做这道题的结果
这道题和线段树模板不同的是,我们的线段树的底部每次都需要新插入一个值,实际上就是对一个值为0的地方作出单点修改。也就是下面的代码:
void pplus(long long p,long long l,long long r,long long w,long long v){//加入函数
if(l==r){t[p]=v;return;}//如果到了底部,直接赋值
long long mid=(l+r)/2;//取中点
if(w<=mid)pplus(p*2,l,mid,w,v);//如果在左边查询左节点
else pplus(p*2+1,mid+1,r,w,v);//同上
t[p]=max(t[p*2],t[p*2+1]);//当前点的值等于两个儿子节点中较大的值
}
如图(我相信关于这一点许多线段树题解都没有写到):
#include<bits/stdc++.h>
using namespace std;
long long modd,m,tt,line;//modd:模数 m:操作数 line:长度 tt:题中t
long long t[2000001];//线段树,存最大值
//十年OI一场空,不开long long……
inline long long read(){//快读优化数据读入(数据大不能忘记)
long long f=1,outt=0;char a=getchar();
while(a>'9'||a<'0'){if(a=='-')f=-1;a=getchar();}
while(a<='9'&&a>='0'){outt*=10;outt+=a-'0';a=getchar();}
return f*outt;
}
void js(long long p,long long l,long long r){//初始化建树
t[p]=-0x3ffffffff;//赋极小值
if(l==r)return;
long long mid=(l+r)/2;
js(p*2,l,mid);//向左节点修改
js(p*2+1,mid+1,r); //向右节点修改
}
long long ask(long long p,long long l,long long r,long long nl,long long nr){//查询函数
if (nl<=l&&r<=nr)return t[p];//如果区间被包含,返回最大值
long long mid=(l+r)/2;//取中点
long long minn=-0x3ffffffff;//赋极小值
if (nl<=mid)minn=ask(p*2,l,mid,nl,nr);//如果查询区间左节点大于等于mid,查询左区间
if (nr>mid)minn=max(minn,ask(p*2+1,mid+1,r,nl,nr));//同上
return minn;//返回最小值
}
void pplus(long long p,long long l,long long r,long long w,long long v){//加入函数
if(l==r){t[p]=v;return;}
long long mid=(l+r)/2;
if(w<=mid)pplus(p*2,l,mid,w,v);
else pplus(p*2+1,mid+1,r,w,v);
t[p]=max(t[p*2],t[p*2+1]);
}
int main(){
m=read(),modd=read();//读入操作数和模数
for(int i=1;i<=m;i++){
char s;//读取操作
cin>>s;
if(s=='A'){
long long ls=read();
line++;//长度++,记录当前值是第几个
pplus(1,1,m,line,(tt+ls)%modd);//将新的值加入线段树
}
else {
long long ls=read();
if(!ls)tt=0;//如果查询为空,直接得到答案
else
tt=ask(1,1,m,line-ls+1,m);//进入查找
printf("%lld\n",tt);
}
}
return 0;//成功AC!
}