首页 > 其他分享 >P5999 [CEOI2016] kangaroo 蓝 题解

P5999 [CEOI2016] kangaroo 蓝 题解

时间:2022-11-18 18:00:10浏览次数:42  
标签:那么 加新块 题解 kangaroo 个块 合并 P5999 块儿 LL

前言

有些题目照常 DP 不是很好做,感觉像是区间 DP,但是怎么设状态都不好转移,那么可以考虑一种维护块儿的 DP,就是这道题要用到的知识点。

背景分析

如果每次跳跃的点的编号形成一个序列的话,那么这个序列满足“波浪形”,即 \(a_1 < a_2,\ a_2>a_3, \ a_3<a_4 \dots\) 或者 \(a_1>a_2,\ a_2<a_3, \ a_3>a_4 \dots\)

那么转化问题,求 \(1\sim n\) 的所有排列中,满足该排列构成的序列是“波浪形”的排列有几个。

前置知识

该题这种 DP 维护的是一个个块,可以设状态 \(f_{i,j}\) 表示填好了 \(1 \sim n\) 中的数,共有了 \(j\) 个块。

比如:\(f_{7,3}\) 表示前 7 个数中共有 3 个块,这 3 个块里面的信息可以是 \([1][2][3,4,5,6,7]\),也可以是 \([4,1,5][2,3][6,7]\),怎样都行。

每新填一个数,这个数可以开一个新块儿,也可以根据这个数合并之前的块儿,我们具体分析。

加新块

如果加数字 \(i\) 之前已经有了 \(j - 1\) 个块,这些块儿会形成 \(j\) 个空,那么这个数字 \(i\) 单独形成的块儿就可以查到这 \(j\) 个空中的一个,所以 \(f_{i,j} = f_{i-1,j-1} \times j\)

合并块

如果加数字 \(i\) 之前已经有了 \(j + 1\) 个块,这些块儿会形成 \(j+2\) 个空,但是,如果你把数字 \(i\) 放到最前面或者最后面,那么因为最前(后)面没有块儿,就无法合并,所以实际上合法的空只有 \(j\) 个。

所以方程是 \(f_{i,j} = f_{i-1,j+1} \times j\)

这道题目

我们依旧是填数,从 \(1\) 填到 \(n\),那么对于当前数 \(i\),之前填的数都比这个数小,之后填的数都比这个数大,那么加新块、合并块这两个操作都是合法的,无论你放在哪里(此时还不考虑 \(s,t\) 的影响

考虑到 \(s\) 和 \(t\),由于最终的块一定合并成了一个,且以 \(s\) 开头、以 \(t\) 结尾,那么他会影响到加新块的操作。

为什么?

如果 \(s\) 已经填好了,那么 \(s\) 所在的位置必然是第一个块的首部,此时你若加新块,必然不能加到 \(s\) 所在块的前面去,因为这样做会使最后合并的块无法满足 \(s\) 开头

如果 \(t\) 已经填好了,那么 \(t\) 所在的位置必然是最后一个块的尾部,此时你若加新块,必然不能加到 \(t\) 所在块的后面去,因为这样做会使最后合并的块无法满足 \(t\) 结尾

那么加新块的状态转移方程为:\(f_{i,j} = f_{i-1,j-1}(j - [i>s] - [i>t])\)

\([\cdots]\) 表示如果中括号内条件满足返回 1,否则返回 0

若 \(i>s\) 满足,则其表示填 \(i\) 的时候,\(s\) 已经填好了,那么第一个块的前面就不能再放了,所以要减一,\(i>t\) 同理。

对于合并块的操作无影响。

那么对于 \(i=s,i=t\) 的情况,该怎么办呢?我们拿 \(i=s\) 作说明,\(i=t\) 同理。

\(i=s\),如果要加新块,必然要加到最前面,所以只有一个位置合法,转移方程 \(f_{i,j} = f_{i-1,j-1} \times 1\);如果要合并块,你也只能把 \(s\) 放到第一个块的最前面,这样做块数是不变的,且只有一个合法位置,转移方程 \(f_{i,j} = f_{i-1,j}\)

Code

#include <iostream>
#include <cstring>

using namespace std;

typedef long long LL;
const LL N = 2e3 + 10;
const LL mod = 1e9 + 7;

LL n, s, t;
LL f[N][N];

int main()
{
    cin >> n >> s >> t;
    f[1][1] = 1;
    for (LL i = 2; i <= n; i ++ )
    {
        for (LL j = 1; j <= i; j ++ )
        {
            if (i == s || i == t)
            {
                f[i][j] = (f[i][j] + f[i - 1][j - 1] + f[i - 1][j]) % mod;
                continue;
            }
            f[i][j] = (f[i][j] + f[i - 1][j - 1] * (j - (i > s) - (i > t))) % mod;
            f[i][j] = (f[i][j] + f[i - 1][j + 1] * j) % mod;
        }
    }
    cout << f[n][1] << endl;
    return 0;
}

尾声

自己做不会,看题解也挺蒙的,搞了一下午才明白。

完结撒花!

标签:那么,加新块,题解,kangaroo,个块,合并,P5999,块儿,LL
From: https://www.cnblogs.com/LittleMoMol-kawayi/p/solution_LuoGu_P5999.html

相关文章

  • 恒创科技:有关服务器虚拟化的常见问题解答
    虚拟化”一词经常使用,尤其是与服务器相关的时候。以下是一些有关服务器虚拟化常见问题的解答。什么是服务器虚拟化?虚拟化是一个经常应用于范围广泛的......
  • helm 问题解决 —— HELM部署异常:Error: UPGRADE FAILED: another operation (install
    转载: https://blog.csdn.net/dreamer_chen/article/details/118596034  HELM部署异常:Error:UPGRADEFAILED:anotheroperation(install/upgrade/rollback)isin......
  • week4题解
    1.深度优先搜索   思路:以固定的移动顺序走迷宫,若能到终点则记一次到终点后回溯到前一个有分岔的地方,走另一条路线若走到死路也同样回溯到前一个有分叉的地方。最......
  • DTOJ-2022-11-10-测试-题解
    题目链接ABCA这个套路已经出现了很多次了就是两条线之间的网格图路径数,做法呢就是容斥题意求满足以下条件的\(n\timesm\)的矩阵的个数对\(10^9+7\)取模对于......
  • vue 项目源码映射失败问题解决
    目录vue项目源码映射失败问题解决前言解决方案效果参考vue项目源码映射失败问题解决前言不知何时起,项目控制台调试进入源代码变成编译后的文件了,调试起来十分不便,强迫......
  • 服务商系统集中高频交易CPU飙升问题解决优化过程
    通过创建数据表索引,有效提升系统性能。一、问题背景在11月10日下午5点,出现channel异步下发消息队列消息积压报警,经排查分析是因为channel请求鑫某亿服务商落单时间过长......
  • Ubuntu常见问题解决
    Ubuntu常见问题解决1.ubuntu系统上安装qt5.12后无法调试运行 原因:缺少gcc、g++、make、libgl1sudoapt-getinstallgccsudoapt-getinstallg++sudoapt-getinstallmak......
  • 2022.11.17模拟赛题解
    从今天起更换码风。猜数字两种做法:二分,哈希二分记函数\(g(x)\)表示数字\(x\)在\(10\)进制下的位数。可以观察到对于正整数\(k(k\ge2)\),都有\(g(k^k)<g((k+1)......
  • LG4778 Counting swaps 题解
    LG4778Countingswaps给定你一个\(1\simn\)的排列\(p\),可进行若干次操作,每次选择两个整数\(x,y\),交换\(p_x,p_y\)。用最少的操作次数将给定排列变成单调上升的序......
  • 今晚题解
    题意概述给定一张\(n\)个点\(m\)条边的有向图\(G\)。有\(n\)个硬币。初始时有的正面朝上,有的反面朝上。每次你可以手动翻转一枚。如果在\(G\)中有边\(a\righ......