问题链接: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. 基于资源编排服务(ROS)实现存量资源的IaC化

    背景 如今,基础设施即代码(Infrastructure as code,IaC)是云资源管理和编排的趋势,基于 IaC 的管理模式,在提升云资源自动化管理能力,降低管理成本的同时,可以大大降低云资源 ...

  2. 基于FFmpeg和Qt实现简易视频播放器

    VideoPlay001 记得一键三连哦 使用qt+ffmpeg开发简单的视频播放器,无声音 视频解码使用的是软解码即只用CPU进行QPainter绘制每一帧图像,CPU占用过高 简单易学,适合小白入 ...

  3. offline RL | IQL:通过 sarsa 式 Q 更新避免 unseen actions

    题目:Offline Reinforcement Learning with Implicit Q-Learning,Sergey Levine 组,2022 ICLR,5 6 8. pdf 版本:h ...

  4. C#中的类和继承

    公众号「DotNet学习交流」,分享学习DotNet的点滴. 类继承 通过继承我们可以定义一个新类,新类纳入一个已经声明的类并进行扩展. 可以使用一个已经存在的类作为新类的基础.已存在的类称为基类(b ...

  5. 七天.NET 8操作SQLite入门到实战 - 第五天引入SQLite-net ORM并封装常用方法(SQLiteHelper)

    前言 上一章节我们搭建好了EasySQLite的前后端框架,今天我们的主要任务是在后端框架中引入SQLite-net ORM并封装常用方法(SQLiteHelper). 七天.NET 8操作SQLit ...

  6. 有哪些可部署的, 无需编程的,基于WEB的数据可视化工具

    基于Web的数据可视化工具在当今数字化时代具有重要的作用,可以帮助企业和个人更好地理解和利用数据.以下是一些无需编程即可部署的基于Web的数据可视化工具,详细描述如下:Tableau Public: ...

  7. [ABC274E] Booster

    Problem Statement In a two-dimensional plane, there are $N$ towns and $M$ chests. Town $i$ is at the ...

  8. 【UniApp】-uni-app-路由

    前言 好,经过上个章节的介绍完毕之后,了解了一下 uni-app-CompositionAPI应用生命周期和页面生命周期 那么了解完了uni-app-CompositionAPI应用生命周期和页面生命 ...

  9. C和C++练习

    要点: 1.数组 2.冒泡排序BubbleSort 3.带指针的结构体(malloc,free) 4.字符串操作(拷贝.逆序.比较) 5.格式化输出printf,sprintf 6.格式化输入,sca ...

  10. 如何给图数据库 NebulaGraph 新增一种数据类型,以 Binary 为例

    NebulaGraph 内核所自带的数据结构其实已经很丰富了,比如 List.Set.Map.Duration.DataSet 等等,但是我们平时在建表和数据写入的时候,可以用到的数据结构其实比较有限 ...