ABC237
ABC237
F
题目大意
求长度为
解题思路
不难判断这道题是一道线性 dp。
先考虑求最长上升子序列的过程:可以把线性扫描序列理解为一个不断在这个序列后加数的过程。
当最长上升子序列被更新时当且仅当加的这个数大于原最长上升子序列的最后一项。
考虑到原来的最长上升子序列最后一项可能会有多值,我们可以只保留最小值,使得任意能更新最长上升子序列的数都必定大于这个值。
又发现题目中只要求维护的最长上升子序列长度为
于是我们便可以设计以下的动态规划:
- 状态表示:
表示长度为 ,长度为 的上升子序列最后一项的最小值为 ,为 的为 ,为 的为 (编码技巧:可以用 表示不存在)。 - 初始化:
。 - 状态转移:原来的三个数为
、 、 ,现加入一个数 ,将第一个比 大的数换成 即可(注意当且仅当 时转移,否则无意义)。
- 答案:满足
的 的和。
代码
#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
题目大意
给出区间升序和区间降序排列两种操作,操作
解题思路
关于区间排序有一个经典 trick,我们可以将大于
为什么?众所周知,排序一次复杂度为
但是这么标了之后由于只剩下
令
- 对于操作
,用线段树将 区间推平为 ,将 区间推平为 ,如果此时数 在区间中,就将它更新为 。 - 对于操作
,用线段树将 区间推平为 ,将 区间推平为 ,如果此时数 在区间中,就将它更新为 。
如此操作,单次操作复杂度就可以做到
代码
#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;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 全程不用写代码,我用AI程序员写了一个飞机大战
· DeepSeek 开源周回顾「GitHub 热点速览」
· 记一次.NET内存居高不下排查解决与启示
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· .NET10 - 预览版1新功能体验(一)