问题链接:Here

长度为 \(N\) 的数列 \(A_1,…,A_N\) 。回答满足以下条件的长度 \(N\) 的数列 \(X_1,…,X_N\) 的个数除以 \(998244353\) 的余数。

  • \(1\le X_i \le A_i\)
  • \(X_i \not = X_{i + 1}\)

\(2\le N \le 5e5,1\le A_i \le 1e9\)


因为 $X_i \not = X_{i + 1} $ 的条件比较难处理,所以用容斥定理来做更好点

如果有 \(K\) 个相邻的相等,那么容斥系数为 \((-1)^k\) 。

那我们把 \(n\) 分为若干连续相同段,然后每一段容斥系数分开算,这样就是一个 dp 的转移式子了。

\[f_i = \sum_{j=0}^{i-1}f_j\times min\{A_k\} (k\in(j,i]\times(-1)^{i-j-1})
\]

这个 \(min\{A_k\}\) 每次加入一个新的时候会影响一个后缀,用单调栈找到这个后缀,然后把 \(f_i\) 丢进线段树里。

而那个容斥系数就是每次整个线段树乘上一个 (-1),这个丢到外面处理就好了

  • \(\mathcal{O}(N\ log\ N)\)​
using ll = long long;
using namespace std;
const ll N = 5e5 + 10, M = N << 2, P = 998244353;
ll n, a[N], q[N], f[N];
ll w[M], lazy[M], v[M];
void Downdata(ll x) {
if (!lazy[x])return;
w[x * 2] = v[x * 2] * lazy[x] % P;
w[x * 2 + 1] = v[x * 2 + 1] * lazy[x] % P;
lazy[x * 2] = lazy[x * 2 + 1] = lazy[x];
return;
}
void Change(ll x, ll L, ll R, ll l, ll r, ll c) {
if (L == l && R == r) {lazy[x] = c; w[x] = v[x] * c % P; return;}
ll mid = (L + R) >> 1; Downdata(x);
if (r <= mid)Change(x * 2, L, mid, l, r, c);
else if (l > mid)Change(x * 2 + 1, mid + 1, R, l, r, c);
else Change(x * 2, L, mid, l, mid, c), Change(x * 2 + 1, mid + 1, R, mid + 1, r, c);
w[x] = (w[x * 2] + w[x * 2 + 1]) % P; v[x] = (v[x * 2] + v[x * 2 + 1]) % P; return;
}
void Insert(ll x, ll L, ll R, ll pos, ll c) {
if (L == R) {v[x] = c; w[x] = c * lazy[x] % P; return;}
ll mid = (L + R) >> 1; Downdata(x);
if (pos <= mid)Insert(x * 2, L, mid, pos, c);
else Insert(x * 2 + 1, mid + 1, R, pos, c);
w[x] = (w[x * 2] + w[x * 2 + 1]) % P; v[x] = (v[x * 2] + v[x * 2 + 1]) % P; return;
}
signed main() {
scanf("%lld", &n);
ll top = 1; Insert(1, 1, n, 1, P - 1);
for (ll i = 1; i <= n; i++) {
scanf("%lld", &a[i]);
while (top > 0 && a[i] < a[q[top]])top--;
Change(1, 1, n, q[top] + 1, i, a[i]); q[++top] = i;
f[i] = (i & 1) ? (P - w[1]) : w[1];
if (i != n)Insert(1, 1, n, i + 1, P - w[1]);
}
printf("%lld\n", f[n]);
return 0;
}

但是这道题是可以线性实现的,题目这个一个点可以控制一段区间的性质,可以考虑用 笛卡尔树 维护,维护一个栈、栈中存储 \(A_i\)​ 的下标和控制的权值两样东西,然后就可以根据奇偶性计算即可

  • \(\mathcal{O}(N)\)
using ll = long long;

// modint
template<int MOD> struct Fp {
ll val;
constexpr Fp(ll v = 0) noexcept : val(v % MOD) {
if (val < 0) val += MOD;
}
constexpr int getmod() const { return MOD; }
constexpr Fp operator - () const noexcept {
return val ? MOD - val : 0;
}
constexpr Fp operator + (const Fp &r) const noexcept { return Fp(*this) += r; }
constexpr Fp operator - (const Fp &r) const noexcept { return Fp(*this) -= r; }
constexpr Fp operator * (const Fp &r) const noexcept { return Fp(*this) *= r; }
constexpr Fp operator / (const Fp &r) const noexcept { return Fp(*this) /= r; }
constexpr Fp &operator += (const Fp &r) noexcept {
val += r.val;
if (val >= MOD) val -= MOD;
return *this;
}
constexpr Fp &operator -= (const Fp &r) noexcept {
val -= r.val;
if (val < 0) val += MOD;
return *this;
}
constexpr Fp &operator *= (const Fp &r) noexcept {
val = val * r.val % MOD;
return *this;
}
constexpr Fp &operator /= (const Fp &r) noexcept {
ll a = r.val, b = MOD, u = 1, v = 0;
while (b) {
ll t = a / b;
a -= t * b, swap(a, b);
u -= t * v, swap(u, v);
}
val = val * u % MOD;
if (val < 0) val += MOD;
return *this;
}
constexpr bool operator < (const Fp &r) const noexcept {
return this->val < r.val;
}
constexpr bool operator == (const Fp &r) const noexcept {
return this->val == r.val;
}
constexpr bool operator != (const Fp &r) const noexcept {
return this->val != r.val;
}
friend constexpr istream &operator >> (istream &is, Fp<MOD> &x) noexcept {
is >> x.val;
x.val %= MOD;
if (x.val < 0) x.val += MOD;
return is;
}
friend constexpr ostream &operator << (ostream &os, const Fp<MOD> &x) noexcept {
return os << x.val;
}
friend constexpr Fp<MOD> modpow(const Fp<MOD> &r, ll n) noexcept {
if (n == 0) return 1;
if (n < 0) return modpow(modinv(r), -n);
auto t = modpow(r, n / 2);
t = t * t;
if (n & 1) t = t * r;
return t;
}
friend constexpr Fp<MOD> modinv(const Fp<MOD> &r) noexcept {
ll a = r.val, b = MOD, u = 1, v = 0;
while (b) {
ll t = a / b;
a -= t * b, swap(a, b);
u -= t * v, swap(u, v);
}
return Fp<MOD>(u);
}
}; const int MOD = 998244353;
using mint = Fp<MOD>;
using pll = pair<ll, ll>; int main() {
cin.tie(nullptr)->sync_with_stdio(false);
int n;
cin >> n;
vector<ll> a(n);
for (int i = 0; i < n; ++i) cin >> a[i]; stack<pll> st;
st.push({0, 0});
vector<mint> dp(n + 1, 0), sdp(n + 2, 0);
dp[0] = 1, sdp[1] = 1;
for (int i = 1; i <= n; ++i) {
ll t = a[i - 1];
while (!st.empty() && st.top().first >= t) st.pop();
ll num = st.top().second;
st.push({t, i}); if (num > 0) dp[i] += dp[num];
dp[i] -= (sdp[i] - sdp[num]) * t;
sdp[i + 1] = sdp[i] + dp[i];
} mint ans = dp[n];
if (n & 1) ans = -ans;
cout << ans;
}

看了一下在推特上这道题的讨论发现线段树写法上还可以在坐标压缩一下,思路来源于 @opt_cp

AtCoder ARC 115 E - LEQ and NEQ (延迟标记线段树 or 笛卡尔积 + DP维护)的更多相关文章

  1. AtCoder Regular Contest 063 F : Snuke’s Coloring 2 (线段树 + 单调栈)

    题意 小 \(\mathrm{C}\) 很喜欢二维染色问题,这天他拿来了一个 \(w × h\) 的二维平面 , 初始时均为白色 . 然后他在上面设置了 \(n\) 个关键点 \((X_i , Y_i ...

  2. Petrozavodsk Winter-2018. AtCoder Contest. Problem I. ADD, DIV, MAX 吉司机线段树

    题意:给你一个序列,需要支持以下操作:1:区间内的所有数加上某个值.2:区间内的所有数除以某个数(向下取整).3:询问某个区间内的最大值. 思路(从未见过的套路):维护区间最大值和区间最小值,执行2操 ...

  3. 【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)

    题面 题目大意: 给你\(m\)张椅子,排成一行,告诉你\(n\)个人,每个人可以坐的座位为\([1,l]\bigcup[r,m]\),为了让所有人坐下,问至少还要加多少张椅子. Solution: ...

  4. ARC115E-LEQ and NEQ【容斥,dp,线段树】

    正题 题目链接:https://atcoder.jp/contests/arc115/tasks/arc115_d 题目大意 \(n\)个数字的序列\(x\),第\(x_i\in [1,A_i]\ca ...

  5. HDU5785 Interesting(Manacher + 延迟标记)

    题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5785 Description Alice get a string S. She think ...

  6. 数据结构--线段树--lazy延迟操作

    A Simple Problem with Integers Time Limit: 5000MS   Memory Limit: 131072K Total Submissions: 53749   ...

  7. FZU 2171(线段树的延迟标记)

    题意:容易理解. 分析:时隔很久,再一次写了一道线段树的代码,之前线段树的题也做了不少,包括各种延迟标记,但是在组队分任务之后,我们队的线段树就交给了另外一个队友在搞, 然后我就一直没去碰线段树的题了 ...

  8. [HDOJ4578]Transformation(线段树,多延迟标记)

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578 四种操作:查询.加法.乘法.改数.应该是需要维护三个lazy标记,然后就是套路了.查询是区间内所 ...

  9. 杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)

    欢迎"热爱编程"的高考少年--报考杭州电子科技大学计算机学院 Just a Hook Time Limit: 4000/2000 MS (Java/Others)    Memor ...

  10. zoj 1610 Count the Colors(线段树延迟更新)

    所谓的懒操作模板题. 学好acm,英语很重要.做题的时候看不明白题目的意思,我还拉着队友一块儿帮忙分析题意.最后确定了是线段树延迟更新果题.我就欣欣然上手敲了出来. 然后是漫长的段错误.... 第一次 ...

随机推荐

  1. 在路上---学习篇(一)Python 数据结构和算法 (5)二分查找、二叉树遍历

    独白: 利用算法进行查找指定元素,最近学习二分查找和二叉树遍历.二分查找前提是在有序中进行查找,二叉树引入了树的概念.树的概念其中有许多小知识点,也是一种新的数据结构.还是之前的感悟,需了解其本质才会 ...

  2. Cadence SPB 22.1 -- 原理图的电器元件放置03Day

    1.新增原理图:"SCHEMATIC"-->"New Page" 2.元器件放置 ①.添加原理图库 ②.放置元器件 选择对应元件库,再选择需要放置的元件, ...

  3. 使用mumu模拟器抓包 andriod app

    背景 公司H5嵌入到农行手机app里面.某天有人反馈进入国内机票订单详情时,应用崩溃了,如下图: 经过测试,此问题仅在安卓手机中出现,且其他页面都正常.于是我怀疑可能是这个页面代码有问题,想着能否抓包 ...

  4. skywalking自定义插件开发

    skywalking是使用字节码操作技术和AOP概念拦截Java类方法的方式来追踪链路的,由于skywalking已经打包了字节码操作技术和链路追踪的上下文传播,因此只需定义拦截点即可. 这里以sky ...

  5. 数据仓库主流开发语言——SQL

    数仓开发语言概述  SQL语言介绍 数仓与SQL 结构化数据 二维表结构 SQL语法分类

  6. 理解 Paimon changelog producer

    介绍 目的 Chaneglog producer 的主要目的是为了在 Paimon 表上产生流读的 changelog, 所以如果只是批读的表是可以不用设置 Chaneglog producer 的. ...

  7. pinia状态管理初识

    一款官方推荐的,代替vuex的,新的状态管理工具. 官方网: https://pinia.vuejs.org/zh/introduction.html 主要区别: 去除了modules的概念,每个st ...

  8. 分享两种Pulsar消息积压topic级别策略老化办法

    本文分享自华为云社区<Pulsar消息积压topic级别策略老化的两种方案>,作者: 张俭. Pulsar像大多数消息中间件一样,支持按时间和大小对消息积压进行老化.但是默认的策略只能在n ...

  9. [BUG]自定义登录引发的配置加载问题-bugspringsecurity

    title: [BUG]自定义登录引发的配置加载问题 date: 2021-12-10 16:43:36.311 updated: 2021-12-27 19:10:16.773 url: https ...

  10. HTTP安全头部对jsp页面不生效

    本文于2016年4月底完成,发布在个人博客网站上. 考虑个人博客因某种原因无法修复,于是在博客园安家,之前发布的文章逐步搬迁过来. 诡异的问题 AppScan扫描报告中提示,Web服务器返回js.cs ...