[题解] [HNOI2016] 序列
题面
给定长度为 的序列:,记为 。类似地,( )是指序列:。若 ,则称 是 的子序列。
现在有 个询问,每个询问给定两个数 和 ,,求 的不同子序列的最小值之和。例如,给定序列
,询问给定的两个数为 和 ,那么 有 个子序列 ,这 个子序列的最小值之和为 。
对于 的数据, , 。
解析
之前考试遇到过一道叫做“序序”的题,求的是子序列最大值和最小值的积的和。这道题是个丐版,实现起来还是比较简单的。
因为要求所有子序列的和,所以考虑猫树分治。就是把所有询问离线出来,然后挂到树上,找到树上的对应区间段。对于整条询问被这个区间段中点截断的左右两部分递归处理,然后左右合并的部分

蓝色部分对应的是查询区间,黑色的部分是猫树(?线段树),将查询下放到树上,可以发现被第一层区间的中点截成两半。
因为被截断的两个区间的答案和本次查询的答案是不影响的,所以被截断的两区间的答案可以继续下放递归处理。 而合并起来后的区间的答案需要在本层计算出来。考虑双指针。
让 从 向左移, 从 向右移,考虑由 构成的区间的最小值来源于哪里。固定 ,在右边找到一个分界点 ,表示当 时,最小值在左边区间取得,当 时,最小值在右边区间取得。当 固定下来后,他的 也确定下来,那么就在 的答案里加上 ,在 的答案里加上 。这个 是右区间每个位置对应的最小值,不是固定的,区间修,区间查,需要用到线段树。因为不同的两种加值对应的系数也不同,所以需要开两棵线段树。并且随着 左移, 的位置是单调向右的。所以单次维护复杂度 。总复杂度 。
code
#include<bits/stdc++.h> using namespace std; #ifdef _WIN32 #define getchar _getchar_nolock #define putchar _putchar_nolock #else #define getchar getchar_unlocked #define putchar putchar_unlocked #endif template <typename T> inline void rd(T &x){ x = 0; int f = 1; char ch = getchar(); while(!isdigit(ch)){ if(ch == '-') f = -1; ch = getchar();} while(isdigit(ch)) x = (x<<1) + (x<<3) + (ch^48), ch = getchar(); x *= f; } template <typename T> inline void wt(T x){ if(x < 0) putchar('-'), x = -x; if(x / 10 > 0) wt(x / 10); putchar(x % 10 + '0'); } #define int long long #define ls (id << 1) #define rs (id << 1 | 1) constexpr int N = 1e5 + 5; int n, q, a[N], ans[N], sum[N<<2], jmn[N], s[N]; struct Seg{ int l, r, ai; }; vector<Seg> spl[N<<2]; vector<int> ful[N<<2]; inline void insert(int id, int l, int r, int x, int y, int ai){ if(l > r) return ; if(x <= l && r <= y) return (void)(ful[id].push_back(ai)); int mid = (l + r) >> 1; if(x <= mid && mid < y) spl[id].push_back(Seg{max(l, x), min(r, y), ai}); if(x <= mid) insert(ls, l, mid, x, y, ai); if(y > mid) insert(rs, mid+1, r, x, y, ai); } namespace ST{ struct node{ int l, r, sum[2], tag[2], val[2]; }t[N<<2]; inline void pushup(int id){ t[id].sum[0] = t[ls].sum[0] + t[rs].sum[0]; t[id].sum[1] = t[ls].sum[1] + t[rs].sum[1]; } inline void addtag(int id, int k, int p){ t[id].tag[p] += k; t[id].sum[p] += k * t[id].val[p]; } inline void pushdown(int id){ if(t[id].tag[0]) addtag(ls, t[id].tag[0], 0), addtag(rs, t[id].tag[0], 0); if(t[id].tag[1]) addtag(ls, t[id].tag[1], 1), addtag(rs, t[id].tag[1], 1); t[id].tag[0] = t[id].tag[1] = 0; } inline void build(int id, int l, int r){ t[id].l = l, t[id].r = r; t[id].val[0] = t[id].val[1] = 0; t[id].sum[0] = t[id].sum[1] = t[id].tag[0] = t[id].tag[1] = 0; if(l == r) return (void)(t[id].val[1] = jmn[l], t[id].val[0] = 1); int mid = (l + r) >> 1; build(ls, l, mid), build(rs, mid+1, r); t[id].val[0] = t[ls].val[0] + t[rs].val[0]; t[id].val[1] = t[ls].val[1] + t[rs].val[1]; pushup(id); } inline void modify(int id, int l, int r, int k, int p){ if(l > r) return; if(l <= t[id].l && t[id].r <= r) return addtag(id, k, p); pushdown(id); int mid = (t[id].l + t[id].r) >> 1; if(l <= mid) modify(ls, l, r, k, p); if(r > mid) modify(rs, l, r, k, p); pushup(id); } inline int query(int id, int l, int r){ if(l <= t[id].l && t[id].r <= r) return t[id].sum[0] + t[id].sum[1]; pushdown(id); int mid = t[ls].r, as = 0; if(l <= mid) as += query(ls, l, r); if(r > mid) as += query(rs, l, r); return as; } } inline void mao(int id, int l, int r){ if(l == r){ sum[id] = a[l]; for(int it : ful[id]) ans[it] += sum[id]; return; } int mid = (l + r) >> 1, imn = a[mid], j = mid + 1, k = 0; sort(spl[id].begin(), spl[id].end(), [](const Seg x, const Seg y){ return x.l > y.l; }); jmn[mid + 1] = a[mid + 1]; s[mid + 1] = a[mid + 1]; for(int i=mid+2; i<=r; ++i) jmn[i] = min(a[i], jmn[i-1]); ST::build(1, mid + 1, r); for(int i=mid; i>=l ;--i){ imn = min(imn, a[i]); while(j <= r && jmn[j] > imn) ++j; ST::modify(1, mid+1, j - 1, imn, 0), ST::modify(1, j, r, 1, 1); while(k < (int)spl[id].size() && spl[id][k].l == i) ans[spl[id][k].ai] += ST::query(1, mid+1, spl[id][k].r), ++k; } sum[id] += ST::query(1, mid+1, r); mao(ls, l, mid), mao(rs, mid+1, r); sum[id] += sum[ls] + sum[rs]; for(int it : ful[id]) ans[it] += sum[id]; } signed main(){ rd(n), rd(q); for(int i=1; i<=n; ++i) rd(a[i]); for(int i=1, x, y; i<=q; ++i) rd(x), rd(y), insert(1, 1, n, x, y, i); mao(1, 1, n); for(int i=1; i<=q; ++i) wt(ans[i]), putchar('\n'); return 0; }
本文作者:XiaoLe_MC
本文链接:https://www.cnblogs.com/xiaolemc/p/18363495
版权声明:本作品采用知识共享署名-非商业性使用-禁止演绎 2.5 中国大陆许可协议进行许可。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步