[噼昂!]sequence(Pending)
壹、關於題目 ¶
有時間再去編罷。
貳、關於題解 ¶
說實話,並不是很會算 “區間的子區間” 這種問題。
但是事實上我們可以採用經典演算法,列舉右端點,檢視每個左端點的貢獻,對於某個詢問 \([L,R]\),其實就是我們列舉所有的 \(r\),計算 \(l\in[L,r]\) 的貢獻再累加即可。
於是這道題我們就有個方向:對於每個右端點都處理出所有左端點對應的最大最小值,這個過程可以使用兩個單調棧來做。對於每個右端點,我們可以使用主席樹維護資訊,每個詢問 \([L,R]\) 實際上就是 \([L,R]\) 版本的線段樹中 \([L,R]\) 的和。
至於如何維護歷史版本的和,似乎可以使用矩陣來維護,不過細節我就不知道了,具體可以摸一摸
然而我並不是很會,於是我用了貓樹分治。具體來說就是對於某區間 \([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; }
肆、關鍵 の 地方 ¶
“區間的子區間”,一般處理方法,移動 左/右端點,用奇怪的方法處理另外一邊的值。或者貓樹分治,處理跨越分支中心的區間對於每個詢問的貢獻。