1003 [JSOI2008]最大数MAXNUMBER 线段树动态插入 查询函数优化
链接:https://ac.nowcoder.com/acm/problem/20164
来源:牛客网
题目描述
现在请求你维护一个数列,要求提供以下两种操作:
1、 查询操作。语法:Q L 功能:查询当前数列中末尾L 个数中的最大的数,并输出这个数的值。限制:L不超过当前数列的长度。
2、 插入操作。语法:A n 功能:将n加 上t,其中t是最近一次查询操作的答案(如果还未执行过查询操作,则t=0),并将所得结果对一个固定的常数D取模,将所得答案插入到数列的末尾。限制:n是非负整数并且在长整范围内。
注意:初始时数列是空的,没有一个数。
输入描述:
第一行两个整数,M和D,其中M表示操作的个数(M ≤ 200,000),D如上文中所述,满足D在longint内。
接下来M行,查询操作或者插入操作。
输出描述:
对于每一个询问操作,输出一行。该行只有一个数,即序列中最后L个数的最大数。
备注:
对于全部的测试点,保证1≤M≤2×105,1≤D≤2×1091 \leq M \leq 2 \times 10^5
,1 \leq D \leq 2 \times 10^91≤M≤2×105,1≤D≤2×109。
分析
言多必失,写多了很容易写错。。所以还是用比较简短的写法写线段树比较好,询问可以优化。直接遍历下去,然后找最值。如果超过范围就返回 0 。
这题就是要判断哪些元素是最后插入的,要返回最后插入的k个元素里最小的元素,考虑动态插入线段树,就不需要考虑那些元素的绝对位置了。
//-------------------------代码---------------------------- #define int ll const int N = 2e5+10; int n,mod,m; struct node { int l,r,mx; } tr[N * 4]; void build(int u,int l,int r) { tr[u]={l,r,0}; if(l==r)return ; int mid=l+r>>1; build(u<<1,l,mid);build(u<<1|1,mid+1,r); } void add(int u,int l,int x) { if(l <= tr[u].l && tr[u].r <= l) { tr[u].mx = max(tr[u].mx,x); rt; } int mid = tr[u].l + tr[u].r >>1; if(l <= mid) add(u<<1,l,x); if(mid < l) add(u <<1|1,l,x); tr[u].mx = max(tr[u<<1].mx,tr[u<<1|1].mx); } int query(int u,int l,int r) { if(l <= tr[u].l && tr[u].r <= r) {return tr[u].mx;} if(r < tr[u].l || l > tr[u].r) return 0; return max(query(u<<1,l,r),query(u<<1|1,l,r)); } void solve() { // cin>>n>>m; cin>>n>>mod; int pre=0,cnt=0; build(1,1,n); for(int i=1;i<=n;i++) { char op;cin>>op; int x;cin>>x; if(op=='A') { cnt++; add(1,cnt,(pre+x)%mod); } else { pre=query(1,cnt-x+1,cnt); cout<<pre<<endl; } } } void main_init() {} signed main(){ AC();clapping();TLE; cout<<fixed<<setprecision(12); main_init(); // while(cin>>n,n) // while(cin>>n>>m,n,m) // int t;cin>>t;while(t -- ) solve(); // {solve(); } return 0; } /*样例区 */ //------------------------------------------------------------