[HNOI2016] 序列

题解:表 + 莫队
- 设莫队维护区间的答案,我们考虑右端点向右扩张时对的影响,设代表区间中的最小值
- 设中最小值的位置为,则,所以
- 我们令为左边比第一个小的位置,为右边比第一个小的位置,,
- ,
- 我们发现左端点在的最小值都是,那么对答案的贡献为
- 以此类推,一定存在一个点,使得
- 所以
- 容易发现
- 所以
- 那么对于一个端点的扩张我们可以更新答案
- 单调栈预处理,表预处理,查询最小值位置,预处理
- 复杂度:
const int N = 1e5 + 10, M = 4e5 + 10;
const int B = sqrt(N) + 1;
int n, q, a[N], nxt[N], pre[N], stk[N], top, fl[N], fr[N];
int id[N], ans[N], Ans, st[N][22], lg2[N];
struct QUERY
{
int l, r, idx;
bool operator<(const QUERY &t) const
{
if (id[l] == id[t.l])
{
if (id[l] & 1)
return r < t.r;
else
return r > t.r;
}
else
return l < t.l;
}
} qry[N];
void build()
{
for (int i = 1; i <= n; ++i)
st[i][0] = i;
for (int i = 2; i <= n; ++i)
lg2[i] = lg2[i >> 1] + 1;
for (int j = 1; j <= 20; ++j)
for (int i = 1; i + (1ll << j) - 1 <= n; ++i)
st[i][j] = (a[st[i][j - 1]] < a[st[i + (1ll << (j - 1))][j - 1]] ? st[i][j - 1] : st[i + (1ll << (j - 1))][j - 1]);
}
int query(int l, int r)
{
int len = lg2[r - l + 1];
return (a[st[l][len]] < a[st[r - (1ll << len) + 1][len]] ? st[l][len] : st[r - (1ll << len) + 1][len]);
}
void del(int l, int r, int op)
{
int p = query(l, r);
if (op == 1)
Ans -= a[p] * (r - p + 1) + fl[l] - fl[p];
else
Ans -= a[p] * (p - l + 1) + fr[r] - fr[p];
}
void add(int l, int r, int op)
{
int p = query(l, r);
if (op == 1)
Ans += a[p] * (r - p + 1) + fl[l] - fl[p];
else
Ans += a[p] * (p - l + 1) + fr[r] - fr[p];
}
void solve()
{
cin >> n >> q;
for (int i = 1; i <= n; ++i)
cin >> a[i];
build();
for (int i = 1, top = 0; i <= n; ++i)
{
while (top && a[stk[top]] >= a[i])
top--;
if (top)
pre[i] = stk[top];
else
pre[i] = 0;
stk[++top] = i;
}
for (int i = n, top = 0; i >= 1; --i)
{
while (top && a[stk[top]] >= a[i])
top--;
if (top)
nxt[i] = stk[top];
else
nxt[i] = n + 1;
stk[++top] = i;
}
for (int i = 1; i <= n; ++i)
fr[i] = fr[pre[i]] + a[i] * (i - pre[i]);
for (int i = n; i >= 1; --i)
fl[i] = fl[nxt[i]] + a[i] * (nxt[i] - i);
for (int i = 1; i <= q; ++i)
{
cin >> qry[i].l >> qry[i].r;
qry[i].idx = i;
}
for (int i = 1; i <= n; ++i)
id[i] = (i - 1) / B + 1;
sort(qry + 1, qry + q + 1);
for (int i = 1, l = 1, r = 0; i <= q; ++i)
{
while (l > qry[i].l)
add(--l, r, 1);
while (r < qry[i].r)
add(l, ++r, 2);
while (l < qry[i].l)
del(l++, r, 1);
while (r > qry[i].r)
del(l, r--, 2);
ans[qry[i].idx] = Ans;
}
for (int i = 1; i <= q; ++i)
cout << ans[i] << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通