题解 序列
首先可以分块做
单调指针+桶排可以做到 \(O(n\sqrt n)\)
然后第一问可以用线段树维护
- 关于一类标记合并线段树:
当每种操作都形如「区间加上一个数」或「区间对一个数取max」时,可以在每个节点维护两个标记 \(tag1\) 和 \(tag2\)
若为叶子节点,则代表这个节点的实际权值应为 \(max(tag1, val_p)+tag2\)
于是区间加 \(dat\) 等价于 \(tag2+=dat\)
区间与 \(dat\) 取max等价于 \(tag1=max(tag1, dat-tag2)\)
注意一个节点的标记下放的时候 \(tag1\) 要先下放
容易看出复杂度是 \(O(nlogn)\) 的
第二问注意一个性质(开始复制题解):
考虑一个点所经过的所有操作序列
显然所有的add操作都对其产生了影响
将add操作合并,这样得到了一个操作序列 \(A_1,M_1,...,A_n,M_n\)
记 \(S_j=\sum\limits_{i\leqslant j} A_i\)
则在 \(M_i\) 操作后进行 \(M_j\) 操作的必要条件是 \(M_i + S_j −S_i <M_j\)
这说明所有进行的max操作是该点上 \(M_i −S_i\) 的一个递增序列
于是可以对每个点用线段树维护单调栈求出有多少个max操作修改了它的值
从一个点到下一个点对应了一些操作的插入与删除,可以用区间修改实现
于是在一棵支持区间修改的线段树上维护单调栈即可
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f3f3f3f3f
#define N 100010
#define ll long long
#define fir first
#define sec second
#define pb push_back
//#define int long long
inline int read() {
int ans=0, f=1; char c=getchar();
while (!isdigit(c)) {if (c=='-') f=-f; c=getchar();}
while (isdigit(c)) {ans=(ans<<3)+(ans<<1)+(c^48); c=getchar();}
return ans*f;
}
int n, m;
char typ[10];
int op[N], l[N], r[N], c[N], a[N], ans2[N], sum[N], now;
ll bit[N], ans1[N];
vector<pair<int, int>> add[N], del[N];
vector<int> que[N];
inline void upd(int i, int val) {for (; i<=m; i+=i&-i) bit[i]+=val;}
inline ll query(int i) {ll ans=0; for (; i; i-=i&-i) ans+=bit[i]; return ans;}
inline void upd2(int i, int val) {for (; i<=m; i+=i&-i) sum[i]+=val;}
inline ll query2(int i) {ll ans=0; for (; i; i-=i&-i) ans+=sum[i]; return ans;}
#define tl(p) tl[p]
#define tr(p) tr[p]
#define val(p) val[p]
#define tag(p) tag[p]
#define tag1(p) tag1[p]
#define tag2(p) tag2[p]
#define maxn(p) maxn[p]
#define rec(p) rec[p]
namespace seg1{
int tl[N<<2], tr[N<<2];
ll val[N<<2], tag1[N<<2], tag2[N<<2];
inline void spread(int p) {
tag1(p<<1)=max(tag1(p<<1), tag1(p)-tag2(p<<1)); tag2(p<<1)+=tag2(p);
tag1(p<<1|1)=max(tag1(p<<1|1), tag1(p)-tag2(p<<1|1)); tag2(p<<1|1)+=tag2(p);
tag1(p)=-INF; tag2(p)=0;
}
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r; tag1(p)=-INF;
if (l==r) {val(p)=a[l]; return ;}
int mid=(l+r)>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
}
void upd1(int p, int l, int r, ll dat) {
if (l<=tl(p)&&r>=tr(p)) {tag1(p)=max(tag1(p), dat-tag2(p)); return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (l<=mid) upd1(p<<1, l, r, dat);
if (r>mid) upd1(p<<1|1, l, r, dat);
}
void upd2(int p, int l, int r, ll dat) {
if (l<=tl(p)&&r>=tr(p)) {tag2(p)+=dat; return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (l<=mid) upd2(p<<1, l, r, dat);
if (r>mid) upd2(p<<1|1, l, r, dat);
}
ll query(int p, int pos) {
if (tl(p)==tr(p)) return max(tag1(p), val(p))+tag2(p);
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (pos<=mid) return query(p<<1, pos);
else return query(p<<1|1, pos);
}
}
namespace seg2{
int tl[N<<2], tr[N<<2], rec[N<<2];
ll maxn[N<<2], tag[N<<2], nowmax;
inline void spread(int p) {
if (!tag(p)) return ;
maxn(p<<1)+=tag(p); tag(p<<1)+=tag(p);
maxn(p<<1|1)+=tag(p); tag(p<<1|1)+=tag(p);
tag(p)=0;
}
int calc(int p, int val) {
if (tl(p)==tr(p)) return maxn(p)>val?1:0;
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (val<=maxn(p<<1)) return calc(p<<1, val)+rec(p);
else return calc(p<<1|1, val);
}
void pushup(int p) {
maxn(p)=max(maxn(p<<1), maxn(p<<1|1));
rec(p)=calc(p<<1|1, maxn(p<<1));
}
int query(int p, int l, int r) {
// cout<<"query: "<<p<<' '<<tl(p)<<' '<<tr(p)<<' '<<l<<' '<<r<<endl;
if (p==1) nowmax=a[now];
if (l<=tl(p)&&r>=tr(p)) {
int t=calc(p, nowmax);
nowmax=max(nowmax, maxn(p));
return t;
}
spread(p);
int mid=(tl(p)+tr(p))>>1, ans=0;
if (l<=mid) ans+=query(p<<1, l, r);
if (r>mid) ans+=query(p<<1|1, l, r);
return ans;
}
void build(int p, int l, int r) {
tl(p)=l; tr(p)=r; maxn(p)=-INF;
if (l==r) return ;
int mid=(tl(p)+tr(p))>>1;
build(p<<1, l, mid);
build(p<<1|1, mid+1, r);
}
void upd(int p, int pos, ll dat) {
if (tl(p)==tr(p)) {maxn(p)=dat; return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (pos<=mid) upd(p<<1, pos, dat);
else upd(p<<1|1, pos, dat);
pushup(p);
}
void upd(int p, int l, int r, ll dat) {
if (l<=tl(p)&&r>=tr(p)) {maxn(p)+=dat; tag(p)+=dat; return ;}
spread(p);
int mid=(tl(p)+tr(p))>>1;
if (l<=mid) upd(p<<1, l, r, dat);
if (r>mid) upd(p<<1|1, l, r, dat);
pushup(p);
}
}
signed main()
{
n=read();
for (int i=1; i<=n; ++i) a[i]=read();
seg1::build(1, 1, n);
m=read();
seg2::build(1, 1, m);
for (int i=1; i<=m; ++i) {
scanf("%s", typ);
if (*typ=='A') {
op[i]=1; l[i]=read(); r[i]=read(); c[i]=read();
if (!c[i]) continue;
seg1::upd2(1, l[i], r[i], c[i]);
add[l[i]].pb({i, 0});
del[r[i]].pb({i, 0});
}
else if (*typ=='M') {
op[i]=2; l[i]=read(); r[i]=read(); c[i]=read();
seg1::upd1(1, l[i], r[i], c[i]);
add[l[i]].pb({i, 1});
del[r[i]].pb({i, 1});
}
else {
op[i]=3; c[i]=read();
que[c[i]].pb(i);
ans1[i]=seg1::query(1, c[i]);
}
}
for (int i=1; i<=n; ++i) {
now=i;
for (auto it:add[i])
if (!it.sec) seg2::upd(1, it.fir, m, -c[it.fir]), upd(it.fir, c[it.fir]), upd2(it.fir, 1);
else seg2::upd(1, it.fir, c[it.fir]-query(it.fir)); //, cout<<"upd: "<<it.fir<<' '<<c[it.fir]-query(it.fir)<<endl;
for (auto it:que[i])
ans2[it]=seg2::query(1, 1, it)+query2(it); //, cout<<"query2: "<<query2(it)<<endl;
for (auto it:del[i])
if (!it.sec) seg2::upd(1, it.fir, m, c[it.fir]), upd(it.fir, -c[it.fir]), upd2(it.fir, -1);
else seg2::upd(1, it.fir, -INF);
}
for (int i=1; i<=m; ++i) if (op[i]==3) printf("%lld %d\n", ans1[i], ans2[i]);
return 0;
}