[噼昂!]sequence(Pending)

壹、关于题目 ¶

有时间再去编罢。

贰、关于题解 ¶

说实话,并不是很会算 “区间的子区间” 这种问题。

但是事实上我们可以采用经典算法,枚举右端点,查看每个左端点的贡献,对于某个询问 \([L,R]\),其实就是我们枚举所有的 \(r\),计算 \(l\in[L,r]\) 的贡献再累加即可。

于是这道题我们就有个方向:对于每个右端点都处理出所有左端点对应的最大最小值,这个过程可以使用两个单调栈来做。对于每个右端点,我们可以使用主席树维护信息,每个询问 \([L,R]\) 实际上就是 \([L,R]\) 版本的线段树中 \([L,R]\) 的和。

至于如何维护历史版本的和,似乎可以使用矩阵来维护,不过细节我就不知道了,具体可以摸一摸 小香猪 . 复杂度为 \(\mathcal O(Cn\log n)\),其中 \(C\) 为矩乘常数。

然而我并不是很会,于是我用了猫树分治。具体来说就是对于某区间 \([L,R]\),处理所有跨越区间 \(mid\) 的询问 \([l,r](l\le mid\land mid<r)\),然后再将这些询问划分为 \([l,mid]\)\([mid + 1, r]\) 即可。特别地,对于 \(l=L\land r=R\) 的情形我们应当再这一层直接计算,否则会超时。

对于跨越区间的询问,我们直接移动左端点,而右端点,我们将其划分为四类:

  • 最大、最小值全在左边;
  • 最大、最小值之一在左边;
  • 没有东西在左边;

看上去有四类,但实际只有三个区间,我们维护右边的最大最小指针,依次移动左指针,处理右端点的询问。

右端点对应值,我们维护其系数就可以了,举个例子,如果最小值在左边,那么我们应该使用线段树维护右边的最大值,在最大值的基础上乘上左边的最小值,加和作为在该左端点下,该右端点的贡献。时间复杂度 \(\mathcal O(n\log^2 n)\).

可能有点抽象,推荐看看代码。代码中的 \(f\) 就是处理覆盖整个区间询问的递推数组。

叁、关于标程 ¶

#pragma GCC optimize("Ofast")

#include <bits/stdc++.h>
using namespace std;
 
// # define USING_STDIN
// # define NDEBUG
// # define NCHECK
#include <cassert>
 
namespace Elaina {

#define rep(i, l, r) for(int i = (l), i##_end_ = (r); i <= i##_end_; ++i)
#define drep(i, l, r) for(int i = (l), i##_end_ = (r); i >= i##_end_; --i)
#define fi first
#define se second
#define mp(a, b) make_pair(a, b)
#define Endl putchar('\n')
#define whole(v) ((v).begin()), ((v).end())
#define bitcnt(s) (__builtin_popcount(s))
#ifdef NCHECK
# define iputs(Content) ((void)0)
# define iprintf(Content, argvs...) ((void)0)
#else
# define iputs(Content) fprintf(stderr, Content)
# define iprintf(Content, argvs...) fprintf(stderr, Content, argvs)
#endif
 
    typedef unsigned int uint;
    typedef long long ll;
    typedef unsigned long long ull;
    typedef pair <int, int> pii;
    typedef pair <ll, ll> pll;
    
    template <class T> inline T fab(T x) { return x < 0 ? -x : x; }
    template <class T> inline void getmin(T& x, const T rhs) { x = min(x, rhs); }
    template <class T> inline void getmax(T& x, const T rhs) { x = max(x, rhs); }
 
#ifndef USING_STDIN
    inline char freaGET() {
# define BUFFERSIZE 1 << 18
        static char BUF[BUFFERSIZE], *p1 = BUF, *p2 = BUF;
        return p1 == p2 && (p2 = (p1 = BUF) + fread(BUF, 1, BUFFERSIZE, stdin), p1 == p2) ? EOF : *p1++;
# undef BUFFERSIZE
    }
# define CHARGET freaGET()
#else
# define CHARGET getchar()
#endif
    template <class T> inline T readret(T x) {
        x=0; int f = 0; char c;
        while((c = CHARGET) < '0' || '9' < c) if(c == '-') f = 1;
        for(x = (c^48); '0' <= (c = CHARGET) && c <= '9'; x = (x << 1) + (x << 3) + (c ^ 48));
        return f ? -x : x;
    }
    template <class T> inline void readin(T& x) { x = readret(T(1)); }
    template <class T, class... Args> inline void readin(T& x, Args&... args) {
        readin(x), readin(args...);
    }

    template <class T> inline void writc(T x, char s = '\n') {
        static int fwri_sta[55], fwri_ed = 0;
        if(x < 0) putchar('-'), x = -x;
        do fwri_sta[++fwri_ed] = x % 10, x /= 10; while(x);
        while(putchar(fwri_sta[fwri_ed--] ^ 48), fwri_ed);
        putchar(s);
    }

} using namespace Elaina;

/**
 * @param MOD used for modulo
 * @param RT the primitive root of @p MOD
*/
template <int MOD, int RT> struct Mint {
    int val;
    static const int mod = MOD;
    Mint(ll v = 0) { val = int(-mod < v && v < mod ? v : v % mod); if(val < 0) val += mod; }
    inline friend bool operator == (const Mint& a, const Mint& b) { return a.val == b.val; }
    inline friend bool operator != (const Mint& a, const Mint& b) { return !(a == b); }
    inline friend bool operator < (const Mint& a, const Mint& b) { return a.val < b.val; }
    inline friend bool operator > (const Mint& a, const Mint& b) { return a.val > b.val; }
    inline friend bool operator <= (const Mint& a, const Mint& b) { return a.val <= b.val; }
    inline friend bool operator >= (const Mint& a, const Mint& b) { return a.val >= b.val; }
    inline Mint& operator += (const Mint& rhs) { return (*this) = Mint((*this).val + rhs.val); }
    inline Mint& operator -= (const Mint& rhs) { return (*this) = Mint((*this).val - rhs.val); }
    inline Mint& operator *= (const Mint& rhs) { return (*this) = Mint(1ll * (*this).val * rhs.val); }
    inline Mint operator - () const { return Mint(-val); }
    inline Mint& operator ++ () { return (*this) = (*this) + 1; }
    inline Mint& operator -- () { return (*this) = (*this) - 1; }
    inline friend Mint operator + (Mint a, const Mint& b) { return a += b; }
    inline friend Mint operator - (Mint a, const Mint& b) { return a -= b; }
    inline friend Mint operator * (Mint a, const Mint& b) { return a *= b; }
    inline friend Mint qkpow(Mint a, ll n) {
        assert(n >= 0); Mint ret = 1;
        for(; n; n >>=1, a *= a) if(n & 1) ret *= a;
        return ret;
    }
    inline friend Mint inverse(Mint a) { assert(a != 0); return qkpow(a, mod - 2); }
};
using mint = Mint <1000000007, 5>;

const int mod = 1000000007;
const int maxn = 1e5;
const int maxq = 1e5;

struct segmtTree {

    mint coe[maxn << 2 ^ 3], sum[maxn << 2 ^ 3];
    mint tag[maxn << 2 ^ 3];

#define ls (i << 1)
#define rs (i << 1 | 1)
#define mid ((l + r) >> 1)
#define _lhs ls, l, mid
#define _rhs rs, mid+1, r

    inline void pushup(int i) { sum[i] = sum[ls] + sum[rs]; }
    inline void add(int i, mint v) {
        sum[i] += coe[i] * v, tag[i] += v;
    }
    inline void pushdown(int i) {
        if(tag[i] == 0) return;
        add(ls, tag[i]), add(rs, tag[i]), tag[i] = 0;
    }
    void build(mint* base, int i, int l, int r) {
        sum[i] = tag[i] = coe[i] = 0;
        if(l == r) return coe[i] = base[l], void();
        build(base, _lhs), build(base, _rhs);
        coe[i] = coe[ls] + coe[rs];
    }
    void modify(int ql, int qr, mint v, int i, int l, int r) {
        if(ql > qr) return;
        if(ql <= l && r <= qr) return add(i, v);
        pushdown(i);
        if(ql <= mid) modify(ql, qr, v, _lhs);
        if(mid < qr)  modify(ql, qr, v, _rhs);
        pushup(i);
    }
    mint query(int ql, int qr, int i, int l, int r) {
        if(ql <= l && r <= qr) return sum[i];
        pushdown(i); mint ret = 0;
        if(ql <= mid) ret = query(ql, qr, _lhs);
        if(mid < qr) ret += query(ql, qr, _rhs);
        return ret;
    }

#undef ls
#undef rs
#undef mid
#undef _lhs
#undef _rhs

} allLeft, mnLeft, mxLeft, noneLeft;

int n, q;
mint a[maxn + 5];

inline void input() {
    readin(n, q);
    rep(i, 1, n) readin(a[i].val);
}

struct query {
    int l, r, id;
    inline bool operator < (const query& rhs) const {
        return l < rhs.l;
    }
};
vector <query> Q[maxn << 2 ^ 3];
inline void getQuery() {
    int l, r;
    rep(i, 1, q) {
        readin(l, r);
        Q[1].push_back({l, r, i});
    }
}

mint one[maxn + 5], mn[maxn + 5], mx[maxn + 5], mnMultiMx[maxn + 5];
mint f[maxn << 2 ^ 3];
mint ans[maxn + 5];
void solve(int id, int l, int r) {
    if(l == r) {
        f[id] = a[l] * a[l];
        for(auto q : Q[id]) ans[q.id] += f[id];
        return;
    }
    int mid = (l + r) >> 1;
    static vector <query> acro; // across mid, pay attention to clear for the static type
    for(auto q : Q[id]) {
        if(q.l == l && q.r == r) continue;
        if(q.l <= mid && mid < q.r)
            acro.emplace_back(q);
    }
    sort(whole(acro));
    mn[mid] = mod - 1, mx[mid] = 0;
    for(int i = mid + 1; i <= r; ++i) {
        one[i] = 1;
        mn[i] = min(mn[i - 1], a[i]);
        mx[i] = max(mx[i - 1], a[i]);
        mnMultiMx[i] = mn[i] * mx[i];
    }
    allLeft.build(one, 1, mid + 1, r);
    mnLeft.build(mx, 1, mid + 1, r);
    mxLeft.build(mn, 1, mid + 1, r);
    noneLeft.build(mnMultiMx, 1, mid + 1, r);
    mint Left_mn = mod - 1, Left_mx = 0;
    int rightMx_ptr = mid + 1, rightMn_ptr = mid + 1;
    for(int i = mid; i >= l; --i) {
        Left_mn = min(Left_mn, a[i]);
        Left_mx = max(Left_mx, a[i]);
        for(; rightMn_ptr <= r && mn[rightMn_ptr] > Left_mn; ++rightMn_ptr);
        for(; rightMx_ptr <= r && mx[rightMx_ptr] < Left_mx; ++rightMx_ptr);
        allLeft.modify(mid+1, min(rightMn_ptr, rightMx_ptr) - 1, Left_mn * Left_mx, 1, mid + 1, r);
        if(rightMn_ptr < rightMx_ptr)
            mxLeft.modify(rightMn_ptr, rightMx_ptr - 1, Left_mx, 1, mid + 1, r);
        if(rightMx_ptr < rightMn_ptr)
            mnLeft.modify(rightMx_ptr, rightMn_ptr - 1, Left_mn, 1, mid + 1, r);
        noneLeft.modify(max(rightMn_ptr, rightMx_ptr), r, 1, 1, mid + 1, r);
        while(!acro.empty() && acro.back().l == i) {
            auto q = acro.back(); acro.pop_back();
            ans[q.id] += allLeft.query(mid + 1, q.r, 1, mid + 1, r);
            ans[q.id] += mnLeft.query(mid + 1, q.r, 1, mid + 1, r);
            ans[q.id] += mxLeft.query(mid + 1, q.r, 1, mid + 1, r);
            ans[q.id] += noneLeft.query(mid + 1, q.r, 1, mid + 1, r);
        }
    }
    assert(acro.empty());
    f[id] += allLeft.query(mid + 1, r, 1, mid + 1, r);
    f[id] += mnLeft.query(mid + 1, r, 1, mid + 1, r);
    f[id] += mxLeft.query(mid + 1, r, 1, mid + 1, r);
    f[id] += noneLeft.query(mid + 1, r, 1, mid + 1, r);
    for(auto q : Q[id]) {
        if(q.l == l && q.r == r) continue;
        if(q.r <= mid) Q[id << 1].push_back(q);
        else if(mid < q.l) Q[id << 1 | 1].push_back(q);
        else {
            Q[id << 1].push_back({q.l, mid, q.id});
            Q[id << 1 | 1].push_back({mid + 1, q.r, q.id});
        }
    }
    solve(id << 1, l, mid);
    solve(id << 1 | 1, mid + 1, r);
    f[id] += f[id << 1] + f[id << 1 | 1];
    for(auto q : Q[id]) if(q.l == l && q.r == r)
        ans[q.id] += f[id];
}

signed main() {
    // freopen("sequence.in", "r", stdin);
    // freopen("sequence.out", "w", stdout);
    input();
    getQuery();
    solve(1, 1, n);
    rep(i, 1, q) writc(ans[i].val);
    return 0;
}

肆、关键 の 地方 ¶

“区间的子区间”,一般处理方法,移动 左/右端点,用奇怪的方法处理另外一边的值。或者猫树分治,处理跨越分支中心的区间对于每个询问的贡献。

posted @ 2021-10-05 21:58  Arextre  阅读(41)  评论(0编辑  收藏  举报