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,英语很重要.做题的时候看不明白题目的意思,我还拉着队友一块儿帮忙分析题意.最后确定了是线段树延迟更新果题.我就欣欣然上手敲了出来. 然后是漫长的段错误.... 第一次 ...
随机推荐
- VLOOKUP函数10种经典用法
VLOOKUP函数是Excel中非常常用的函数之一,可以用于在一个区域或表格中查找某个值,并返回该值所在行的另一个指定列中的数值.以下是VLOOKUP函数的10种经典用法: 基本的VLOOKUP用法: ...
- 明解Java第二章练习题答案
练习2-1 请对代码清单2-6的2处进行修改,将带有小数部分的实数值赋给x和y,查看结果如何? 答案:编译器会报错,因为小数的默认类型double是比int更大的类型.如果给double类型的变量赋值 ...
- Hnswlib 介绍与入门使用
Hnswlib是一个强大的近邻搜索(ANN)库, 官方介绍 Header-only C++ HNSW implementation with python bindings, insertions a ...
- OkHttp3发送http请求
导入依赖 <!-- https://mvnrepository.com/artifact/com.squareup.okhttp3/okhttp --> <dependency> ...
- Linux的文件系统,根目录rootfs结构
文件系统fs 文件系统时操作系统用来管理文件的.fs=filesystem... 在linux中,一切皆为文件,这句话够经典了吧... linux中每个分区都是一个fs. FHS Linux下的Fil ...
- 春秋云镜 - CVE-2022-30887
多语言药房管理系统 (MPMS) 是用 PHP 和 MySQL 开发的, 该软件的主要目的是在药房和客户之间提供一套接口,客户是该软件的主要用户.该软件有助于为药房业务创建一个综合数据库,并根据到期. ...
- SQL语句(mysql)「一」
SQL的一些常用语句 创建类 CREAT DATABASE <数据库名>; 该方法创建一个数据库,当要使用一个数据库的时候,使用指令: USE <数据库名>; 查看当前正在使用 ...
- Ubuntu 之 7zip使用
1.安装 sudo apt-get install p7zip 2.压缩 7zr a xxx foldername 3.解压缩 7zr x xxx.7z 4.zip命令压缩文件夹 zip -qr xx ...
- 文心一言 VS 讯飞星火 VS chatgpt (53)-- 算法导论6.2 5题
五.MAX-HEAPIFY的代码效率较高,但第 10 行中的递归调用可能例外,它可能使某些编译器产生低效的代码.请用循环控制结构取代递归,重写 MAX-HEAPIFY代码. 文心一言: 以下是使用循环 ...
- GaussDB(for MySQL)新特性TDE发布:支持透明数据加密
本文分享自华为云社区<GaussDB(for MySQL)新特性TDE发布:支持透明数据加密>,作者: GaussDB 数据库. 技术背景 为了保护数据的安全,我们可能通过防火墙.身份认证 ...