\(\text{I love Wordle!!!}\)

I guessed this 5-letter word in 3/6 tries.

⬛⬛⬛⬛⬛
🟩⬛⬛⬛⬛
🟩🟩🟩🟩🟩

Can you guess this word?

\(\frak{Description}\)

\(\rm Link.\)

\(\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\). 那么递推式就是

\[f[i]=f[pos]+a[i]\times (i-pos) \]

关于 \(pos\),用单调栈预处理即可。

我们找出 \([l,r+1]\) 中最小值的下标 \(p\),类似上面的 \(\mathtt{dp}\) 就有

\[\Delta=a[p]\times (p-l+1)+f[r+1]-f[p] \]

由于 \(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;
}
posted on 2020-03-12 11:13  Oxide  阅读(106)  评论(0编辑  收藏  举报