2021暑期hdu多校3

老鸽子了

A

吐槽

出题人的符号管理乱七八遭

E Didn't I Say to Make My Abilities Average in the Next Life?!

吐槽

二次元题目

看完题解感觉自己是...

题意

给定一个序列,\(q\)组询问,每次询问一个区间的所有子区间的最大值的和

题解

题解的两个做法感觉都有点复杂,看不太懂,自己写一个

离线从左到右扫描线,当我们扫到点\(r\)时,我们对于每个点\(l\),维护\(max_r[l]\)表示\([l,r]\)中的最大值,那么\(\sum max_i[l]\)将表示\([l,l], [l,l+1],...,[l,r]\)的答案的和,从而\(\sum_{l \leq L \leq r} \sum max_i[L]\)将是答案

我们可以用线段树来做到这一点,理清之后还挺好写的

#include <bits/stdc++.h>
using namespace std;
    
#define gc getchar
inline int read() {
    int p = 0, w = 1; char c = gc();
    while(c > '9' || c < '0') { if(c == '-') w = -1; c = gc(); }
    while(c >= '0' && c <= '9') { p = p * 10 + c - '0'; c = gc(); }
    return p * w;
}

#define rep(io, st, ed) for(int io = st; io <= ed; io ++)

const int mod = 1e9 + 7;
const int sid = 1e6 + 5;
const int inf = 1e9 + 5;

inline void inc(int &a, int b) { a += b; if(a >= mod) a -= mod; }
inline void dec(int &a, int b) { a -= b; if(a < 0) a += mod; }
inline int Inc(int a, int b) { return (a + b >= mod) ? a + b - mod : a + b; }
inline int Dec(int a, int b) { return (a - b < 0) ? a - b + mod : a - b; }
inline int mul(int a, int b) { return 1ll * a * b % mod; }
inline int fp(int a, int k) { 
    int ret = 1; 
    while(k) { 
        if(k & 1) ret = mul(ret, a); 
        a = mul(a, a); k >>= 1; 
    }
    return ret;
}

struct query {
    int l, id;
    query() {}
    query(int _l, int _id) : l(_l), id(_id) {}
} ;
vector <query> Q[sid];

int n, m;
int a[sid],  ans[sid];
int lst_time[sid], sumtag[sid], sum[sid];
int nowv[sid], presum[sid], pret[sid], newt[sid];

#define ls (o << 1)
#define rs (o << 1 | 1)
void build(int o, int l, int r) {
    lst_time[o] = sumtag[o] = sum[o] = 0;
    nowv[o] = presum[o] = pret[o] = newt[o] = 0;
    if(l == r) return;
    int mid = (l + r) >> 1;
    build(ls, l, mid);
    build(rs, mid + 1, r);
}

void down(int o, int l, int r, int nv, int ps, int pt, int nt) {
    inc(sum[o], 1ll * sumtag[o] * (pt - lst_time[o]) % mod);
    inc(sum[o], 1ll * ps * (r - l + 1) % mod);
    if(!nowv[o])  // 先前没有标记
        nowv[o] = nv, presum[o] = ps, pret[o] = pt, newt[o] = nt; 
    else {
        inc(presum[o], ps);
        inc(presum[o], 1ll * nowv[o] * (pt - newt[o]) % mod);
        newt[o] = nt; nowv[o] = nv;
    }
    lst_time[o] = nt; sumtag[o] = mul(r - l + 1, nv);
}

void pushdown(int o, int l, int r) {
    if(!nowv[o]) return;
    int mid = (l + r) >> 1;
    down(ls, l, mid, nowv[o], presum[o], pret[o], newt[o]);
    down(rs, mid + 1, r, nowv[o], presum[o], pret[o], newt[o]);
    nowv[o] = 0; presum[o] = 0;
}

void pushup(int o, int l, int r, int t) {
    inc(sum[o], 1ll * (t - lst_time[o]) * sumtag[o] % mod);
    sumtag[o] = Inc(sumtag[ls], sumtag[rs]);
    lst_time[o] = t;
}

void mdf(int o, int l, int r, int ml, int mr, int v, int t) {
    if(ml > r || mr < l) return;
    if(ml <= l && mr >= r) { down(o, l, r, v, 0, t, t); return; }
    int mid = (l + r) >> 1;
    pushdown(o, l, r);
    mdf(ls, l, mid, ml, mr, v, t);
    mdf(rs, mid + 1, r, ml, mr, v, t);
    pushup(o, l, r, t);
}

int qry(int o, int l, int r, int ml, int mr, int t) {
    if(ml > r || mr < l) return 0;
    if(ml <= l && mr >= r) return Inc(sum[o], 1ll * sumtag[o] * (t-lst_time[o]) % mod);
    int mid = (l + r) >> 1;
    pushdown(o, l, r);
    return Inc(qry(ls, l, mid, ml, mr, t), qry(rs, mid + 1, r, ml, mr, t));
}

int st[sid], top;
void segment_work(int opt = 0) {
    a[0] = -inf; top = 0;
    build(1, 1, n);
    rep(r, 1, n) {
        while(top && a[r] >= a[ st[top] ]) top --;
        mdf(1, 1, n, st[top] + 1, r, (a[r] + mod) % mod, r - 1);
        st[++ top] = r;
        for(auto x : Q[r]) {
            int l = x.l, id = x.id;
            if(!opt) inc(ans[id], qry(1, 1, n, l, r, r));
            else dec(ans[id], qry(1, 1, n, l, r, r));
            if(opt) {
                int len = r - l + 1;
                ans[id] = mul(ans[id], fp(1ll * len * (len + 1) % mod, mod - 2));
            }
        }
    }
}

void solve() {
    n = read(); m = read();
    rep(i, 1, n) a[i] = read(), Q[i].clear();
    rep(i, 1, m) {
        int l = read(), r = read();
        Q[r].push_back( query(l, i) );
        ans[i] = 0;
    }
    segment_work();
    rep(i, 1, n) a[i] = -a[i];
    segment_work(1);
    rep(i, 1, m) printf("%d\n", ans[i]);
}

int main() {
    int T = read();
    while(T --) solve();
    return 0;
}

G Increasing Subsequence

题意

给定一个排列,求极大上升子序列的数量

题解

\(n^2\)做法不难想到,设\(f[i]\)表示\(a_1,...,a_i\)中的极大上升子序列的数量,考虑转移,\(j\)能转移到\(i\),当且仅当\(a_j < a_i\),并且区间\([j+1, i-1]\)中,没有在\([a_j,a_i]\)中的数

抽象一下,也即能转移到\(f[i]\)\(j\)将构成一个满足下标\(j<i\),权值\(a_j < a_i\)的数构成的单调栈

我们可以考虑用分治解决下标这一维,对于\([l, r]\),考虑\([l, mid]\)\([mid + 1, r]\)的贡献

由于分治,\([mid + 1, r]\)中的元素不存在下标上的影响,对权值上的影响,只需要预处理出\([mid + 1, r]\)中每个数左边比它小的最大数即可

对于\([l, mid]\)而言,我们按权值大小来建立单调栈,右边相应的按照权值在单调栈上二分即可