ABC237

F

题目大意

求长度为 \(n\) 且最长上升子序列长度恰好为 \(3\) 的序列 \(a\) 的个数(\(1 \leq a_i \leq m\))。

解题思路

不难判断这道题是一道线性 dp。

先考虑求最长上升子序列的过程:可以把线性扫描序列理解为一个不断在这个序列后加数的过程。

当最长上升子序列被更新时当且仅当加的这个数大于原最长上升子序列的最后一项。

考虑到原来的最长上升子序列最后一项可能会有多值,我们可以只保留最小值,使得任意能更新最长上升子序列的数都必定大于这个值。

又发现题目中只要求维护的最长上升子序列长度为 \(3\),且 \(a_i\) 范围很小,我们便可以在 \(dp\) 中用三维分别表示长度为 \(1\)、\(2\)、\(3\) 的最长上升子序列末尾最小值。

于是我们便可以设计以下的动态规划:

  • 状态表示:\(dp[i][x][y][z]\) 表示长度为 \(i\),长度为 \(1\) 的上升子序列最后一项的最小值为 \(x\),为 \(2\) 的为 \(y\) ,为 \(3\) 的为 \(z\)(编码技巧:可以用 \(m+1\) 表示不存在)。
  • 初始化:\(dp[0][m + 1][m + 1][m + 1]=1\)。
  • 状态转移:原来的三个数为 \(x\) 、 \(y\) 、 \(z\) ,现加入一个数 \(j\),将第一个比 \(j\) 大的数换成 \(j\) 即可(注意当且仅当 \(x \leq y \leq z\) 时转移,否则无意义)。
\[\begin{cases}
dp[i][j][y][z]=dp[i][j][y][z] + dp[i - 1][x][y][z]\text{, $j \leq x$}\\
dp[i][x][j][z]=dp[i][x][j][z] + dp[i - 1][x][y][z]\text{, $x < j \leq y$}\\
dp[i][x][y][j]=dp[i][x][y][j] + dp[i - 1][x][y][z]\text{, $y < j \leq z$}
\end{cases}
\]
  • 答案:满足 \(x<y<z\) 的 \(dp[n][x][y][z]\) 的和。

代码

#include<bits/stdc++.h>
#define endl "\n"
#define ll long long
using namespace std; const int N = 1010, P = 998244353; int n, m;
ll dp[N][20][20][20], ans; int main()
{
ios :: sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m;
dp[0][m + 1][m + 1][m + 1] = 1;
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= m; j++)
{
for (int x = 1; x <= m + 1; x++)
{
for (int y = x; y <= m + 1; y++)
{
for (int z = y; z <= m + 1; z++)
{
if (j <= x)
{
(dp[i][j][y][z] += dp[i - 1][x][y][z]) %= P;
}
else if (j <= y)
{
(dp[i][x][j][z] += dp[i - 1][x][y][z]) %= P;
}
else if (j <= z)
{
(dp[i][x][y][j] += dp[i - 1][x][y][z]) %= P;
}
}
}
}
}
}
for (int i = 1; i <= m; i++)
{
for (int j = i + 1; j <= m; j++)
{
for (int k = j + 1; k <= m; k++)
{
(ans += dp[n][i][j][k]) %= P;
}
}
}
cout << ans << endl;
return 0;
}

G

题目大意

给出区间升序和区间降序排列两种操作,操作 \(m\) 次后,求原排列中 \(x\) 现在的位置。

解题思路

关于区间排序有一个经典 trick,我们可以将大于 \(x\) 的数标为 \(1\),小于等于 \(x\) 的数标为 0。

为什么?众所周知,排序一次复杂度为 \(O(n\log n)\),难以接受。

但是这么标了之后由于只剩下 \(0\) 和 \(1\),我们便可以使用线段树统计 \(1\) 个数。

令 \(1\) 的个数为 \(sum\)。

  1. 对于操作 \(1\) ,用线段树将 \([l, r - sum]\) 区间推平为 \(0\),将 \([r - sum + 1, r]\) 区间推平为 \(1\),如果此时数 \(x\) 在区间中,就将它更新为 \(r - sum\)。
  2. 对于操作 \(2\) ,用线段树将 \([l, l + sum - 1]\) 区间推平为 \(1\),将 \([l + sum, r]\) 区间推平为 \(0\),如果此时数 \(x\) 在区间中,就将它更新为 \(l + sum\)。

如此操作,单次操作复杂度就可以做到 \(O(\log n)\),一共\(O(m\log n)\),可以通过本题。

代码

#include<bits/stdc++.h>
#define endl "\n"
#define ls x << 1
#define rs x << 1 | 1
using namespace std; const int N = 2e5 + 10; int n, m, p, ans;
int a[N]; struct SGT
{
int lc[N << 2], rc[N << 2];
long long dat[N << 2], tag[N << 2]; inline void push_up(int x)
{
dat[x] = dat[ls] + dat[rs];
} inline void push_down(int x)
{
if (!tag[x])
{
return;
}
if (tag[x] == 1)
{
dat[ls] = rc[ls] - lc[ls] + 1;
dat[rs] = rc[rs] - lc[rs] + 1;
}
else
{
dat[ls] = dat[rs] = 0;
}
tag[ls] = tag[rs] = tag[x];
tag[x] = 0;
return;
} void build(int x, int l, int r)
{
lc[x] = l, rc[x] = r;
tag[x] = 0;
if (l == r)
{
dat[x] = a[l] > p;
return;
}
int mid = (l + r) / 2;
build(ls, l, mid);
build(rs, mid + 1, r);
push_up(x);
return;
} void modify(int x, int l, int r, int k)
{
if (l > rc[x] || r < lc[x])
{
return;
}
if (lc[x] >= l && rc[x] <= r)
{
dat[x] = k * (rc[x] - lc[x] + 1);
tag[x] = k ? 1 : -1;
return;
}
push_down(x);
modify(ls, l, r, k);
modify(rs, l, r, k);
push_up(x);
return;
} long long ask(int x, int l, int r)
{
if (l > rc[x] || r < lc[x])
{
return 0;
}
if (lc[x] >= l && rc[x] <= r)
{
return dat[x];
}
push_down(x);
return ask(ls, l, r) + ask(rs, l, r);
}
} t; int main()
{
ios :: sync_with_stdio(0);
cin.tie(0), cout.tie(0);
cin >> n >> m >> p;
for (int i = 1; i <= n; i++)
{
cin >> a[i];
if (a[i] == p)
{
ans = i;
}
}
t.build(1, 1, n);
for (int i = 1; i <= m; i++)
{
int op, l, r;
cin >> op >> l >> r;
int sum = t.ask(1, l, r);
if (op == 1)
{
t.modify(1, l, r - sum, 0);
t.modify(1, r - sum + 1, r, 1);
if (l <= ans && ans <= r)
{
ans = r - sum;
}
}
else
{
t.modify(1, l, l + sum - 1, 1);
t.modify(1, l + sum, r, 0);
if (l <= ans && ans <= r)
{
ans = l + sum;
}
}
}
cout << ans << endl;
return 0;
}

ABC237的更多相关文章

  1. 2022年5月5日模拟赛题解与总结(ABC237)

    总结 初一第一,竞赛班第二 还可以,为了照顾提高班来的四个同学放了四个水题,可惜他们做的不是很理想,希望他们下次可以获得满意的成绩 这次做的其实是 AtCoder ABC237 A.Not Overf ...

随机推荐

  1. Codeforces 777 题目研讨

    题目连接 A B C D E 题目分析 A 难度:普及− 题面翻译: 给你三张牌:\(0\),\(1\),\(2\). 最初选一张,然后依次进行 \(n\) 次交换,交换规则为:中间一张和左边的一张, ...

  2. AI 实战篇:Spring-AI再更新!细细讲下Advisors

    在2024年10月8日,Spring AI再次进行了更新,尽管当前版本仍为非稳定版本(1.0.0-M3),但博主将持续关注这些动态,并从流行的智能体视角深入解析其技术底层.目前,Spring AI仍处 ...

  3. C# 开发的环境监测上位机应用

    前言 在工业和科研领域,环境监测系统的重要性日益凸显.上位机软件作为环境监测系统的关键组成部分,负责数据采集.处理和显示,对提高监测效率和准确性起着至关重要的作用. 本文将向大家介绍一款用 C# 开发 ...

  4. 数据库研发人员必看的MySQL 8.0新特性

    本文汇总了MySQL8.0 面向开发的新特性,总共有12个新特性,有想快速了解8.0新特性的朋友,可以看一下哈文章目录:1.公用表达式支持-CTE2.窗口函数3.表达式作为默认值:4.CHECK支持5 ...

  5. Spring + EHcache配置

    需要使用Spring来实现一个Cache简单的解决方案,具体需求如下:使用任意一个现有开源Cache Framework,要求可以Cache系统中Service或则DAO层的get/find等方法返回 ...

  6. 数据同步之DataX

    目前业务中需要进行数据同步, 考虑使用datax数据同步方式替换掉现有的同步方式 业务场景: 即将业务中每天生成的日志表中的数据部分字段同步到自己的库中,进行后台数据的查询 起因: 之前"大 ...

  7. 抓包工具之Charles(mac)

    下载地址:https://www.charlesproxy.com/download/ 因为软件是收费的,所以破解方式可以参考:https://www.zzzmode.com/mytools/char ...

  8. PCA主成分分析的Python实现

    技术背景 PCA主成分分析在数据处理和降维中经常被使用到,是一个非常经典的降维算法,本文提供一个PCA降维的流程分解,和对应的Python代码实现. 二维数据生成 如果没有自己的测试数据,我们可以生成 ...

  9. linux web终端wetty食用方法

    学校有些机房电脑性能贼垃但又不得不去那些机房上课 我也不想带电脑,于是弄台廉价的服务器 本来想拿个公网ip配frp连我电脑完事 想到vim是在终端中运行的编辑器 于是想弄个web终端然后就找到了wet ...

  10. Excel VLOOKUP 函数入门教程

    一.引言 在日常工作中,Excel 是处理数据的强大工具之一,而 VLOOKUP 函数作为 Excel 中最常用的查找函数之一,能够帮助用户在表格或数值数组中快速定位并返回所需的数据.它的工作原理是在 ...