AtCoder ARC 115 E - LEQ and NEQ (延迟标记线段树 or 笛卡尔积 + DP维护)
问题链接: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 的转移式子了。
\]
这个 \(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维护)的更多相关文章
- AtCoder Regular Contest 063 F : Snuke’s Coloring 2 (线段树 + 单调栈)
题意 小 \(\mathrm{C}\) 很喜欢二维染色问题,这天他拿来了一个 \(w × h\) 的二维平面 , 初始时均为白色 . 然后他在上面设置了 \(n\) 个关键点 \((X_i , Y_i ...
- Petrozavodsk Winter-2018. AtCoder Contest. Problem I. ADD, DIV, MAX 吉司机线段树
题意:给你一个序列,需要支持以下操作:1:区间内的所有数加上某个值.2:区间内的所有数除以某个数(向下取整).3:询问某个区间内的最大值. 思路(从未见过的套路):维护区间最大值和区间最小值,执行2操 ...
- 【题解】 AtCoder ARC 076 F - Exhausted? (霍尔定理+线段树)
题面 题目大意: 给你\(m\)张椅子,排成一行,告诉你\(n\)个人,每个人可以坐的座位为\([1,l]\bigcup[r,m]\),为了让所有人坐下,问至少还要加多少张椅子. Solution: ...
- ARC115E-LEQ and NEQ【容斥,dp,线段树】
正题 题目链接:https://atcoder.jp/contests/arc115/tasks/arc115_d 题目大意 \(n\)个数字的序列\(x\),第\(x_i\in [1,A_i]\ca ...
- HDU5785 Interesting(Manacher + 延迟标记)
题目 Source http://acm.hdu.edu.cn/showproblem.php?pid=5785 Description Alice get a string S. She think ...
- 数据结构--线段树--lazy延迟操作
A Simple Problem with Integers Time Limit: 5000MS Memory Limit: 131072K Total Submissions: 53749 ...
- FZU 2171(线段树的延迟标记)
题意:容易理解. 分析:时隔很久,再一次写了一道线段树的代码,之前线段树的题也做了不少,包括各种延迟标记,但是在组队分任务之后,我们队的线段树就交给了另外一个队友在搞, 然后我就一直没去碰线段树的题了 ...
- [HDOJ4578]Transformation(线段树,多延迟标记)
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4578 四种操作:查询.加法.乘法.改数.应该是需要维护三个lazy标记,然后就是套路了.查询是区间内所 ...
- 杭电 HDU ACM 1698 Just a Hook(线段树 区间更新 延迟标记)
欢迎"热爱编程"的高考少年--报考杭州电子科技大学计算机学院 Just a Hook Time Limit: 4000/2000 MS (Java/Others) Memor ...
- zoj 1610 Count the Colors(线段树延迟更新)
所谓的懒操作模板题. 学好acm,英语很重要.做题的时候看不明白题目的意思,我还拉着队友一块儿帮忙分析题意.最后确定了是线段树延迟更新果题.我就欣欣然上手敲了出来. 然后是漫长的段错误.... 第一次 ...
随机推荐
- 明解Java第二章练习题答案
练习2-1 请对代码清单2-6的2处进行修改,将带有小数部分的实数值赋给x和y,查看结果如何? 答案:编译器会报错,因为小数的默认类型double是比int更大的类型.如果给double类型的变量赋值 ...
- 1、GO语言入门-环境准备
1.Windows开发环境准备 (1)Golang编译器下载 golang中文网:https://studygolang.com/dl 或者:https://go.dev/dl/ (2)下载解压,找到 ...
- 文心一言 VS 讯飞星火 VS chatgpt (160)-- 算法导论12.4 2题
二.用go语言,请描述这样一棵有 n 个结点的二叉搜索树,其树中结点的平均深度为 O(lgn),但这棵树的高度是w(lgn).一棵有 n个结点的二叉搜索树中结点的平均深度为 O(lgn),给出这棵树高 ...
- [研究]SpringBoot-MybatisPlus-Dynamic(多数据源)
SpringBoot-MybatisPlus-Dynamic(多数据源) 前言 基于工作上班累死了...打开自己电脑 不知道干些啥 就康康 PL 网站康康 更新了啥 咦~~~还挺多 看到了多数 ...
- Python——第二章:基础数据类型
下面是需要掌握的知识点: int, float, bool (5星)str (5星)list (5星)tuple (2星)set (1星)dict (5星)bytes ...
- fence的使用
一.创建一个集群及pcs安装 1.真机切换root用户下 2.打开PC管理器视图 1.安装pcs,关掉防火墙,重启pcs和下次开机自动启动pcs 1.创建一个集群,用户:hacluster:密码:re ...
- Docker + Jenkins 如何实现自动化部署?
Docker + Jenkins 如何实现自动化部署? 一. 概述 实验室每次项目发布测试时,都要手动本地打包好了然后上传到服务器,替换原来nginx下面的目录文件,十分麻烦和繁琐.这次就来优化一下, ...
- 文心一言 VS 讯飞星火 VS chatgpt (126)-- 算法导论11.1 1题
一.用go语言,假设一动态集合 S用一个长度为 m 的直接寻址表T来表示.请给出一个查找 S 中最大元素的过程.你所给的过程在最坏情况下的运行时间是多少? 文心一言,代码正常运行: 在这个问题中,我们 ...
- C++标准库中copy算法的使用
目录 std::copy是C++标准库中的算法接口,主要用于两个容器间的复制,据说其效率要优于自己用for循环逐个复制.之前一直非常混淆其中的用法,这里总结了几个例子如下: #include < ...
- C# 查找PDF页面指定区域中的文本并替换和高亮
对PDF文档中的内容进行查找时,可针对文档全篇内容获取查找结果,也可在PDF指定页面中的特定范围内(矩形框区域)进行查找,对获取的查找结果可执行文本高亮或替换等操作,本文将对此作相关介绍(附VB.NE ...