1. 程式人生 > 其它 >[噼昂!]sequence(Pending)

[噼昂!]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;
}

肆、關鍵 の 地方 ¶

“區間的子區間”,一般處理方法,移動 左/右端點,用奇怪的方法處理另外一邊的值。或者貓樹分治,處理跨越分支中心的區間對於每個詢問的貢獻。