\(\text{I love Wordle!!!}\)
I guessed this 5-letter word in 3/6 tries.
⬛⬛⬛⬛⬛
🟩⬛⬛⬛⬛
🟩🟩🟩🟩🟩
Can you guess this word?
\(\frak{Description}\)
\(\frak{Solution}\)
\(\text{Method 1}\):莫队
试试莫队?既然是莫队,我们就应该先考虑如何向外拓展。先假设原区间为 \([l,r]\),我们拓展至 \(r+1\). 那么贡献就是 \(∑\min\{[i,r+1]\}\ (l\le i\le r+1)\).
设 \(f[i]\) 为以 \(i\) 为结尾的所有区间的 \(\min\) 之和。\(f[i]\) 挺好算,我们找到 \(i\) 前面第一个比 \(a[i]\) 小的数的下标记为 \(pos\). 那么递推式就是
关于 \(pos\),用单调栈预处理即可。
我们找出 \([l,r+1]\) 中最小值的下标 \(p\),类似上面的 \(\mathtt{dp}\) 就有
由于 \(a[p]\) 是 \([l,r+1]\) 中最小值,所以 \((p,r+1]\) 区间内的值对包含位置 \(p\) 的区间没有任何影响。所以 \(f[r+1]-f[p]\) 正好是左端点在 \((p,r+1]\) 的区间的答案。
类似地,我们设 \(g[i]\) 为以 \(i\) 为开始的所有区间的 \(\min\) 之和。这里就不再赘述。
时间复杂度 \(\mathcal O(n\sqrt n)\).
接下来我们谈谈莫队玄学的坑:
- 首先四个循环时我们应该先加 \(R\),再加 \(L\);先减 \(L\),再减 \(R\)。不然可能会出现 \(L>R\) 的悲惨情况。尤其是莫队的奇偶优化,有时循环打错了会造成朴素莫队过得了,优化过不了的情况(应该是优化使得右端点回退次数多了,造成 \(L>R\))。
- 有些题虽然可以用
long long
输出,但是中途莫队可能会爆,所以满足上述操作时,保险将加值与减值轮换操作。
\(\text{Method 2}\):猫树
将询问像在线段树上挂起来,每个节点计算过 \(\rm mid\) 的区间答案,主要是利用了当左端点向左移动时,\(\min\) 在 \(\rm mid\) 左半拉和在右半拉的右端点正好可以被分成两端,用两棵线段树分别维护。细节就不讲了 qwq,是 \(\mathcal O(n\log^2 n)\) 的。
\(\text{Method 3}\):线段树
看到计算 \(\sum_{i=l}^r\sum_{j=i}^r f(i,j)\) 肯定思考历史和。向右移动右端点,线段树上是左端点的信息,对每个节点维护 \(v\) 表示当前右端点为 \(r=i\) 时,左端点在区间 \([l,r]\) 的区间的最小值之和,\(\rm his\) 即为历史和,维护 \(\rm tag\) 为当前 \(v\) 要贡献几次到 \(\rm his\),再维护 \(\rm la\) 为最小值之和的懒标记。
考虑算法过程,用单调栈维护最小值统治的区间,它有个非常美好的性质:假设需要将最小值改成 \(k\),你会发现统治区间的最小值 \(k'\) 原本是相同的,所以可以将 "区间覆盖" 变成 "区间加",区间内每个数的增量即为 \(k-k'\). 维护完单调栈后,将 \([1,i]\) 中的 \(\rm tag\) 加一,表示历史和的累加。
显然我们需要先下放 \(\rm la\) 再下放 \(\rm tag\) 以保证累加给历史和的是正确的最小值之和。然而这里有一个问题:假设 \(v\) 对应着累加 \(\rm tag\) 次,此时又下放了新的 \(\rm la\) 标记,就会把新增标记也累加不必要的次数。解决方法是再维护变量 \(\rm dec\),每次下放 \(\rm la\) 标记时将多加的部分累加到 \(\rm dec\) 上即可。复杂度是 \(\mathcal O(n\log n)\) 的。
\(\text{Method 4}\):\(\rm cdq\) 分治
细节看上去贼 \(\rm jb\) 多,但是把 \(l,r\) 的范围转化成矩形还是挺妙的,看这篇吧:\(\rm Link.\)
\(\frak{Code}\)
\(\text{Method 1}\):莫队
#include <cmath>
#include <cstdio>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 1000100, lim = 2e9;
int rmq[N][22], lg[N], q[N], n, m, a[N], pos[N], block, minn[N][2];
ll ans, cnt[N], res[N], f[N][2];
struct node {
int l, r, id;
}s[N];
int read() {
int x = 0, f = 1; char s;
while((s = getchar()) > '9' || s < '0') if(s == '-') f = -1;
while(s >= '0' && s <= '9') {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
return x * f;
}
int Cmp(const int x, const int y) {return a[x] <= a[y] ? x : y;}
bool cmp(const node a, const node b) {return (pos[a.l] ^ pos[b.l]) ? a.l < b.l : ((pos[a.l] & 1) ? a.r < b.r : a.r > b.r);}
int get(const int l, const int r) {
int len = r - l + 1;
return Cmp(rmq[l][lg[len]], rmq[r - (1 << lg[len]) + 1][lg[len]]);
}
void rig(const int l, const int r, const ll op) {
int p = get(l, r);
ans += 0ll + op * (p - l + 1) * a[p] + op * (f[r][0] - f[p][0]);
}
void lef(const int l, const int r, const ll op) {
int p = get(l, r);
ans += 0ll + op * (r - p + 1) * a[p] + op * (f[l][1] - f[p][1]);
}
void MD() {
int L = 1, R = 0;
for(int i = 1; i <= m; ++ i) {
while(R < s[i].r) rig(L, R + 1, 1ll), ++ R;
while(L < s[i].l) lef(L, R, -1ll), ++ L;
while(L > s[i].l) lef(L - 1, R, 1ll), -- L;
while(R > s[i].r) rig(L, R, -1ll), -- R;
res[s[i].id] = ans;
}
}
void init() {
block = sqrt(n);
for(int i = 1; i <= n; ++ i) pos[i] = (i - 1) / block + 1;
int l = 1, r = 1; a[0] = a[n + 1] = -lim;
for(int i = 1; i <= n; ++ i) {
while(l <= r && a[q[r]] >= a[i]) -- r;
minn[i][0] = q[r];
q[++ r] = i;
}
l = 1, r = 1; q[1] = n + 1;
for(int i = n; i >= 1; -- i) {
while(l <= r && a[q[r]] >= a[i]) -- r;
minn[i][1] = q[r];
q[++ r] = i;
}
for(int i = 1; i <= n; ++ i) f[i][0] = f[minn[i][0]][0] + 1ll * a[i] * (i - minn[i][0]);
for(int i = n; i >= 1; -- i) f[i][1] = f[minn[i][1]][1] + 1ll * a[i] * (minn[i][1] - i);
for(int i = 2; i <= n; ++ i) lg[i] = lg[i >> 1] + 1;
for(int i = 1; i <= n; ++ i) rmq[i][0] = i;
for(int j = 1; j <= 20; ++ j)
for(int i = 1; i <= n; ++ i)
rmq[i][j] = Cmp(rmq[i][j - 1], rmq[i + (1 << j - 1)][j - 1]);
}
int main() {
n = read(), m = read();
for(int i = 1; i <= n; ++ i) a[i] = read();
for(int i = 1; i <= m; ++ i) s[i].l = read(), s[i].r = read(), s[i].id = i;
init();
sort(s + 1, s + m + 1, cmp);
MD();
for(int i = 1; i <= m; ++ i) printf("%lld\n", res[i]);
return 0;
}
\(\text{Method 3}\):线段树
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' || s<'0')
f |= (s=='-');
while(s>='0' && s<='9')
x = (x<<1)+(x<<3)+(s^48),
s = getchar();
return f?-x:x;
}
template <class T>
inline void write(T x) {
static int writ[40],w_tp=0;
if(x<0) putchar('-'),x=-x;
do writ[++w_tp]=x-x/10*10,x/=10; while(x);
while(w_tp) putchar(writ[w_tp--]^48);
}
#include <algorithm>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
ll ans[maxn];
int n,m,a[maxn],stk[maxn],tp;
struct Query {
int l,r,id;
bool operator < (const Query& t) const {
return r<t.r;
}
} q[maxn];
struct node {
ll his,tag,v,la,dec;
} t[maxn<<2];
void build(int o,int l,int r) {
if(l==r) return t[o].v=a[l], void();
int mid = l+r>>1;
build(o<<1,l,mid); build(o<<1|1,mid+1,r);
t[o].v = t[o<<1].v+t[o<<1|1].v;
}
void adj(int o,ll k,int L,bool opt=0) {
t[o].dec += k;
if(opt) t[o].his += k*L;
}
void addv(int o,ll k,int L) {
if(t[o].tag) adj(o,-k*t[o].tag,L);
t[o].la += k, t[o].v += k*L;
}
void addt(int o,ll k) {
t[o].tag += k, t[o].his += k*t[o].v;
}
void pushUp(int o) {
t[o].v = t[o<<1].v+t[o<<1|1].v;
t[o].his = t[o<<1].his+t[o<<1|1].his;
}
void pushDown(int o,int L) {
if(t[o].la) {
addv(o<<1,t[o].la,L-(L>>1));
addv(o<<1|1,t[o].la,L>>1);
t[o].la=0;
}
if(t[o].tag) {
addt(o<<1,t[o].tag);
addt(o<<1|1,t[o].tag);
t[o].tag=0;
}
if(t[o].dec) {
adj(o<<1,t[o].dec,L-(L>>1),1);
adj(o<<1|1,t[o].dec,L>>1,1);
t[o].dec=0;
}
}
void modify(int o,int l,int r,int L,int R,int k) {
if(l>=L && r<=R) return addv(o,k,r-l+1), void();
int mid = l+r>>1; pushDown(o,r-l+1);
if(L<=mid) modify(o<<1,l,mid,L,R,k);
if(R>mid) modify(o<<1|1,mid+1,r,L,R,k);
pushUp(o);
}
ll query(int o,int l,int r,int L,int R) {
if(l>=L && r<=R) return t[o].his;
int mid = l+r>>1; ll ret=0; pushDown(o,r-l+1);
if(L<=mid) ret = query(o<<1,l,mid,L,R);
if(R>mid) ret += query(o<<1|1,mid+1,r,L,R);
return ret;
}
void pushTag(int o,int l,int r,int R) {
if(r<=R) return addt(o,1), void();
int mid = l+r>>1; pushDown(o,r-l+1);
pushTag(o<<1,l,mid,R);
if(mid<R) pushTag(o<<1|1,mid+1,r,R);
pushUp(o);
}
int main() {
n=read(9), m=read(9);
for(int i=1;i<=n;++i) a[i]=read(9);
for(int i=1;i<=m;++i)
q[i].id = i,
q[i].l=read(9), q[i].r=read(9);
sort(q+1,q+m+1); build(1,1,n); int pos=0;
for(int i=1;i<=n;++i) {
while(tp && a[stk[tp]]>=a[i]) {
modify(1,1,n,stk[tp-1]+1,stk[tp],a[i]-a[stk[tp]]); --tp;
}
stk[++tp]=i; pushTag(1,1,n,i);
while(pos<m && q[pos+1].r==i)
++pos, ans[q[pos].id] = query(1,1,n,q[pos].l,i);
}
for(int i=1;i<=m;++i) print(ans[i],'\n');
return 0;
}