[噼昂!]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;
}
肆、关键 の 地方 ¶
“区间的子区间”,一般处理方法,移动 左/右端点,用奇怪的方法处理另外一边的值。或者猫树分治,处理跨越分支中心的区间对于每个询问的贡献。