1012: [JSOI2008]最大数maxnumber
Time Limit: 3 Sec Memory Limit: 162 MBSubmit: 2284 Solved: 1039
[ Submit][ Status][ Discuss]
Description
Input
第一行两个整数,M和D,其中M表示操作的个数(M <= 200,000),D如上文中所述,满足(0Output
Sample Input
A 96
Q 1
A 97
Q 1
Q 2
Sample Output
9693
96
评测链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1012
解法1:线段树,即本篇所讲。
解法2:单调队列+并查集:http://blog.youkuaiyun.com/yuyanggo/article/details/8844625
解法3:单调队列+二分查找:http://blog.youkuaiyun.com/yuyanggo/article/details/8845023
我的程序比较复杂一点。
我是先把所有的操作都读入进去,用数组q[]来记录。这样的话,我就可以记录一个n(表示总共的插入次数,即总点数,用来优化线段树的查询区间),记录一个last(表示最后一次询问是第几个操作,优化最后只有操作但无询问的情况)。
然后,我用一个f【】数组记录线段树中,某节点代表的区间中是否有有值被修改(即是否有新节点插入),用布尔变量qa(初始为0)来记录上一个操作是否是插入操作,读入一段连续的插入操作,直到遇到第一个询问操作时,才对线段树中的节点值进行修改。
当然,还的用len来记录当前已插入多少个元素,对于操作Q L,实际上就是求线段树中区间[len-L+1,len]的最大值。
代码:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cctype>
#define maxn (200000+100)
using namespace std;
struct tnode{char s;int l;}q[maxn];//记录操作
int n=0,len=0,last=0,d;
int tree[maxn*3];//tree[i]记录节点i代表的区间中的最大值
bool f[maxn*3];//记录区间中是否有节点的值被修改(即是否有新节点插入)
void init()
{
freopen("maxnumber.in","r",stdin);
freopen("maxnumber.out","w",stdout);
}
void readdata()
{
int m,i;
scanf("%d%d\n",&m,&d);
for(i=1;i<=m;i++)
{
scanf("%c%d\n",&q[i].s,&q[i].l);
if(q[i].s=='A')n++;
}
for(i=m;i>0;i--)
if(q[i].s=='Q'){last=i;break;}
}
//查找区间【i,i】在线段树中的节点位置
inline int wei(int i)
{
int p=1,l=1,r=n,m,k;
while(l<r)
{
m=(l+r)>>1,k=p<<1;
if(i<=m)p=k,r=m;
else p=k+1,l=m+1;
}
return p;
}
//对区间中子节点值有变化的点进行染色标记
inline void ranse(int i)
{
int k=i>>1;
while(k>1 &&!f[k])
f[k]=1,k=k>>1;
}
//对线段树中,子节点值有变化的节点的值进行修改
void build(int p,int l,int r)
{
int m=(l+r)>>1,k=p<<1;
if(f[k])build(k,l,m),f[k]=0;
if(f[k+1])build(k+1,m+1,r),f[k+1]=0;
tree[p]=max(tree[k],tree[k+1]);
}
//在区间[pl,pr]中查询被包含在区间【l,r】中的部分的最大值
int get(int p,int pl,int pr,int l,int r)
{
if(l<=pl && pr<=r)return tree[p];
int m=(pl+pr)>>1,k=p<<1;
if(r<=m)return get(k,pl,m,l,r);
if(l>m)return get(k+1,m+1,pr,l,r);
return max(get(k,pl,m,l,r),get(k+1,m+1,pr,l,r));
}
void work()
{
int t=0,i,k; bool qa=0;
memset(f,0,sizeof(f));
for(i=1;i<=last;i++)
if(q[i].s=='A')
{
len++;k=wei(len); qa=1;
tree[k]=(q[i].l+t)%d;//感觉还是把tree记录为long long好一点,但既然过了就懒得去修改了
ranse(k);
}
else
{
if(qa && n>1)build(1,1,n);
t=get(1,1,n,len-(q[i].l)+1,len); qa=0;
printf("%d\n",t);
}
}
int main()
{
init();
readdata();
work();
return 0;
}