雅礼国庆集训 day1 T2 折射
题面
算法
转化题意
说白了就是给了你一堆点,让你数这种折线有多少个 (严格向下走,并且横坐标之间的差越来越小)

看着像一种在 y 轴方向排序的 dp
但是由于是折线, 所以需要加一维来判断转向
dp 设计
状态设计
\(dp_{i, 0/1}\) 表示第 i 个点, 是向左下还是右上
状态转移方程
最初想到从 y 轴, 从上往下递推, 但是这样满足不了抽象的 x 轴约束, 因此考虑从 x 轴方向递推
于是有递推式子
\[dp_{i, 0} = dp_{i, 0} + dp_{j, 1} ( y_j < y_i )
\]
\[dp_{j, 1} = dp_{i, 0} + dp_{j, 1} ( y_j > y_i )
\]
边界条件
\[dp_{i, 0} = 1, dp_{i, 1} = 1
\]
时间复杂度
优秀啊
没看懂, 这题之前完全没看懂, 怎么敢就这样的
无敌无敌
不难发现对折射点按照 \(y_i\) 降序排序, 一组解在这个序列中一定有序
如何约束 \(x\)?
不难想到对上一个点和上上个点的约束做处理, 怎么用 \(\rm{dp}\) 处理?
这个可以直接 \(f_{i, p, q}\) 转移, 然后 \(\mathcal{O} (n^2)\) 枚举前缀和优化一下即可
空间就滚动 + 离散化即可
哎哎哎, 怎么当时没人这么写, 不得不理一下自己打一份了
\[ \begin{align*}
\begin{cases}
f_{i, p, q} \gets f_{i - 1, p, q} \\
f_{i, p, q} \gets
\sum_{k = 1}^{p - 1} f_{i - 1, k, q}
\end{cases}
\end{align*} \]
第二个简单前缀和一下即可
总共 \(\mathcal{O} (n^2)\)
代码
#include <bits/stdc++.h>
const int MAXN = 1e5 + 20;
const int MOD = 1e9 + 7;
int n, m;
int dp[MAXN][2];
int ans = 0;
struct node
{
int x, y;
friend bool operator < (node a, node b)
{
return a.x < b.x;
}
} p[MAXN];
int main()
{
scanf("%d", &n);
for (int i = 1; i <= n; i++)
{
scanf("%d %d", &p[i].x, &p[i].y);
}
std::sort(p + 1, p + n + 1);
for (int i = 1; i <= n; i++)
{
dp[i][0] = dp[i][1] = 1;
for (int j = i - 1; j >= 1; j--)
{
if (p[i].y > p[j].y)
{
dp[i][0] = (dp[i][0] + dp[j][1]) % MOD;
}
else
{
dp[j][1] = (dp[i][0] + dp[j][1]) % MOD;
}
}
}
for (int i = 1; i <= n; i++)
{
ans = (ans + (dp[i][0] + dp[i][1] - 1) % MOD) % MOD;
}
printf("%d\n", ans);
return 0;
}
总结
这说明了有些看似顺序很尴尬的题目,说不定瞎搞搞就能减少一个数量级的复杂度

浙公网安备 33010602011771号