Loading

雅礼国庆集训 day1 T2 折射

题面

题面下载

算法

转化题意

说白了就是给了你一堆点,让你数这种折线有多少个 (严格向下走,并且横坐标之间的差越来越小)
pAGu6Sg.png

看着像一种在 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 \]

时间复杂度

优秀啊


没看懂, 这题之前完全没看懂, 怎么敢就这样的
无敌无敌

题意

n6000n \leq 6000 个折射点
要求找到一组折射点形如
pAGu6Sg.png
也就是要求每次都在下行 &\& 收紧的折射点集合数量

不难发现对折射点按照 \(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;
}

总结

这说明了有些看似顺序很尴尬的题目,说不定瞎搞搞就能减少一个数量级的复杂度

posted @ 2024-10-07 18:44  Yorg  阅读(22)  评论(0)    收藏  举报