P4891 序列
[P4891 序列]{https://www.luogu.org/problemnew/show/P4891}
毒瘤线段树
区间维护:
\(pa:a<b的a积\)
\(pb:a>=b的b积\)
\(max\_a:最大a\)
\(min\_b:a<b中最小b\)
\(cnt:a<b的个数\)
\(lazy:标记下传\)
这里面的\(a\)是题目的\(C\)把\(C\)省去,和前缀和差不多
首先看\(b\),确实单点修改没什么好讲的,找到最底层修改一下
这题难点主要在\(a_i\),影响到的区间为\(i+1,n\),线段树二分找到\(max_a<val\)的区间,将\(val\)放到能成为最大值的区间在传\(lazy\)标记,
每次操作后的答案为\(tree[root].pa*tree[root].pb\)
**My complete code: **
#include<cstdio>
#include<vector>
using namespace std;
typedef long long LL;
LL n,m,q,maxn,id;
LL rt[27000000],ls[27000000],rs[27000000],d[27000000];
vector<LL> G[300010];
inline LL MAX(LL g1,LL g2){
return g1>=g2?g1:g2;
}
LL query(LL &now,LL l,LL r,LL x){
if(!now)
now=++id;
if(l==r)
return l;
LL mid=(l+r)>>1;
LL shu=mid-l+1-d[ls[now]];
if(x<=shu)
return query(ls[now],l,mid,x);
else
return query(rs[now],mid+1,r,x-shu);
}
void del(LL &now,LL l,LL r,LL x){
if(!now)
now=++id;
d[now]++;
if(l==r)
return;
LL mid=(l+r)>>1;
if(x<=mid)
del(ls[now],l,mid,x);
else del(rs[now],mid+1,r,x);
}
inline LL del_hang(LL x){
LL pos=query(rt[n+1],1,maxn,x);
del(rt[n+1],1,maxn,pos);
return pos>n?
G[n+1][pos-n-1]:
1ll*pos*m;
}
inline LL del_lie(LL x,LL y){
LL pos=query(rt[x],1,maxn,y);
del(rt[x],1,maxn,pos);
return pos<m?
1ll*(x-1)*m+1ll*pos:
G[x][pos-m];
}
int main(){
scanf("%lld%lld%lld",&n,&m,&q);
maxn=MAX(n,m)+q;
for(int i=1;i<=q;++i){
LL x,y;
scanf("%lld%lld",&x,&y);
if(y==m){
LL id=del_hang(x);
G[n+1].push_back(id);
printf("%lld\n",id);
}else{
LL id=del_lie(x,y);
printf("%lld\n",id);
G[n+1].push_back(id);
id=del_hang(x);
G[x].push_back(id);
}
}
return 0;
}/*
*/