@description@

给定初始集合为 1 ~ N 的全集,并给定一个 K。

每次对于当前集合 S,你可以选择 S 中的一个元素 x,并将 x 从 S 中删除。

假如 x - 2 在 1 ~ N 的范围内且不在集合 S 中,在 S 中加入 x - 2。

假如 x + K 在 1 ~ N 的范围内且不在集合 S 中,在 S 中加入 x + K。

求最后可以得到的不同集合数量 mod M。

Constraints

1≤K≤N≤150, 108≤M≤109

Input

输入格式如下:

N K M

Output

输出不同集合数量 mod M。

Sample Input 1

3 1 998244353

Sample Output 1

7

@solution@

考虑假如 x 与 x-2 最后都要被删除,肯定应该先删 x 再删 x-2(先删 x-2 的话,再删 x 就又多出来一个 x-2)。

那么我们连边 x -> x-2,x -> x+K,表示 x 比 x-2, x+K 先删。

最后如果要求删除的点形成一个环,肯定无解。否则我们按照拓扑序来删必然是一个合法方案。

那么相当于对于这样一个图,有多少点集满足点集内的点不形成环。

考虑与 K 无关的那些边,会连成 1 <- 3 <- ... 与 2 <- 4 <- ... 两条链,一条奇数,一条偶数。

接下来将 K 分奇偶讨论,因为 K 是偶数时 x -> x+K 必然是在奇偶内部连边,而 K 为奇数时可以跨奇偶连边。

当 K 为偶数时,我们要避免 a -> a-2 -> ... a-K -> a 这样的环,其实就是不能选择超过 K/2 + 1 个连续点。

这个随便怎么 dp 都可以。

当 K 为奇数时。注意到最小环必然恰好经过 2 条 K 边(0,1 显然,> 2 可以缩成 2 条)。

我们先将图写成类似以下形式(假设 K = 3):

其实就是对于每个奇数 x,将 x 与 x + K 放在同一层。

这样有什么好处呢?注意到环的形式一定为 a -> a-2 -> ... b-K -> b -> b-2 -> ... a-K -> a(假设 a 为奇数),对应到图上即从 a 开始往上走到某一点 b-K,再往右走到 b,再往上走到 a-K 的地方。

等价地说,假如这样一条往上 + 往右 + 往上的路径包含 > K + 1 个点,就会形成环。

注意到这样一条路径没有往下的选择,所以我们就可以从上到下 dp。

定义 dp(i, j, k) 表示前 i 层,往上 + 往右 + 往上的路径包含 j 个点,右边偶数的链对应往上的点连续选中了 k 个。

k 这一维是为了方便我们得到新的 j(可能在 i 这个点直接往右走)。

注意往上 + 往右 + 往上,两个往上可以缩减成一个点,但必须要有往右的过程。

@accepted code@

#include <cstdio>
#include <algorithm>
using namespace std;
const int MAXN = 150;
int N, K, M;
int add(int a, int b) {return (a + b)%M;}
int mul(int a, int b) {return 1LL*a*b%M;}
int f[MAXN + 5][MAXN + 5];
void solve1() {
K /= 2, f[0][0] = 1;
for(int i=1;i<=N;i++) {
for(int j=0;j<=K;j++)
f[i][0] = add(f[i][0], f[i-1][j]);
for(int j=0;j<K;j++)
f[i][j+1] = add(f[i][j+1], f[i-1][j]);
}
int ans1 = 0, ans2 = 0;
for(int i=0;i<=K;i++)
ans1 = add(ans1, f[N/2][i]);
for(int i=0;i<=K;i++)
ans2 = add(ans2, f[(N+1)/2][i]);
printf("%d\n", mul(ans1, ans2));
}
int g[2*MAXN + 5][MAXN + 5][MAXN + 5];
void solve2() {
int p; g[0][0][0] = 1;
for(int i=2;i-K<=N;i+=2) {
for(int j=0;j<=N;j++)
for(int k=0;k<=K+1;k++)
g[i][0][0] = add(g[i][0][0], g[i-2][j][k]);
if( i <= N ) {
for(int j=0;j<=N;j++)
for(int k=0;k<=K+1;k++)
g[i][j+1][0] = add(g[i][j+1][0], g[i-2][j][k]);
}
if( i - K >= 1 ) {
for(int j=0;j<=N;j++) {
for(int k=1;k<=K;k++)
g[i][0][k+1] = add(g[i][0][k+1], g[i-2][j][k]);
g[i][0][0] = add(g[i][0][0], g[i-2][j][0]);
}
}
if( i <= N && i - K >= 1 ) {
for(int j=0;j<=N;j++)
for(int k=0;max(k,j+1)<=K;k++)
g[i][j+1][max(k+1,j+2)] = add(g[i][j+1][max(k+1,j+2)], g[i-2][j][k]);
}
p = i;
}
int ans = 0;
for(int j=0;j<=N;j++)
for(int k=0;k<=K+1;k++)
ans = add(ans, g[p][j][k]);
printf("%d\n", ans);
}
int main() {
scanf("%d%d%d", &N, &K, &M);
if( K % 2 == 0 ) solve1();
else solve2();
}

@details@

感觉 AGC 好像很喜欢出这种状态定义比较抽象,但是状态转移非常简单的 dp 题。

比如 AGC039E,或者说 AGC037D 都是这种类型的 dp。

你以为你绕了半天写出来的长代码就是正解了?

拜托,正解根本不足 100 行.jpg。

但是做了这么多 AGC 的 dp 题还是不会 QAQ。

@atcoder - AGC035E@ Develop的更多相关文章

  1. 【AtCoder】AtCoder Grand Contest 035 解题报告

    点此进入比赛 \(A\):XOR Circle(点此看题面) 大致题意: 给你\(n\)个数,问是否能将它们摆成一个环,使得环上每个位置都是其相邻两个位置上值的异或值. 先不考虑\(0\),我们假设环 ...

  2. AtCoder Grand Contest 035

    Preface Atcoder的题都好劲啊,都是我做不动的计数与构造 就当锻炼自己的思维能力了(基本都是bzt教的) A - XOR Circle bzt说这题数据太水了只要判一下所有数异或值是否为\ ...

  3. Codeforces & Atcoder神仙题做题记录

    鉴于Codeforces和atcoder上有很多神题,即使发呆了一整节数学课也是肝不出来,所以就记录一下. AGC033B LRUD Game 只要横坐标或者纵坐标超出范围就可以,所以我们只用看其中一 ...

  4. PLSQL Develop PlugIn 之脚本自动匹配补全工具CnPlugin

    插件位置:百度云 -- 开发工具空间 -- CnPlugin CnPlugin 支持PL/sql Developer 7.0以上版本,它可以根据 关键字+tab/space 来触发代码补全,而关键字. ...

  5. How to Develop blade and soul Skills

    How to Develop Skills Each skill can be improved for variation effects. Some will boost more strengt ...

  6. Server Develop (七) Linux 守护进程

    守护进程 守护进程,也就是通常说的Daemon进程,是Linux中的后台服务进程.它是一个生存期较长的进程,通常独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件.守护进程常常在系统引导装 ...

  7. android network develop(1)----doing network background

    Develop network with HttpURLConnection & HttpClient. HttpURLConnection  is lightweight with Http ...

  8. Gitlab的develop角色的人没有权限无法提交的问题解决方案

    问题 事情是这样的,最近跟几位同事搞一些东西,打算在Gitlab上建一个仓库,然后协同开发. 我建好仓库,将其他几位同事添加进来,角色分配为Develop. 之后提交初始代码到master分支后,他们 ...

  9. 国内android帮助文档镜像网站---http://wear.techbrood.com/develop/index.html

    http://wear.techbrood.com/develop/index.html

随机推荐

  1. body-parser与querystring与multer的区别

    body-parser express中间件 body-parser是用来解析http请求体的,是express默认使用的中间件之一. (只解析post的普通数据请求,无法解析post文件请求) 使用 ...

  2. 【笔记】Python3导入包规则

    例如:这里给出了一种可能的包结构(在分层的文件系统中): sound/ 顶层包 __init__.py 初始化 sound 包 formats/ 文件格式转换子包 __init__.py wavrea ...

  3. 珂朵莉树(ODT)笔记

    珂朵莉树,又叫老司机树($Old\, Driver \, Tree$) 是一种暴力出奇迹,就怕数据不随机的数据结构. 适用 需要用线段树维护一些区间修改的信息…… 像是区间赋值(主要),区间加…… 原 ...

  4. linux 修改命令提示符&ubuntu目录颜色个性化

    1.linux 命令提示符个性化 在 ANSI 兼容终端(例如 xterm.rxvt.konsole 等)里, 可以用彩色显示文本而不仅仅是黑白. 本文示范了如何使用粗体和彩色的文字.     相信每 ...

  5. Leetcode24.Swap Nodes in Pairs两两交换链表中的节点

    给定一个链表,两两交换其中相邻的节点,并返回交换后的链表. 示例: 给定 1->2->3->4, 你应该返回 2->1->4->3. 说明: 你的算法只能使用常数的 ...

  6. 强强联合 阿里云 RDS for SQL Server 与 金蝶 K/3 WISE 产品实现兼容适配

    强强联合 阿里云 RDS for SQL Server 与 金蝶 K/3 WISE 产品实现兼容适配,原K/3 WISE用户通过简单配置就可以无缝搭配RDS SQL Server使用,不需再费时费力自 ...

  7. Vue--moment时间格式插件安装和使用

    使用链接:http://momentjs.cn/ 1.安装monent 2.导入 3.过滤器   4.template模板使用:

  8. web前端学习(四)JavaScript学习笔记部分(3)-- JavaScript函数+异常处理+事件处理

    1.Javascript函数-了解函数的用途 1.1.函数: 函数是由事件驱动的或者当它被调用时执行的可重复使用的代码块 2.Javascript函数-定义函数 2.1.function必须小写 3. ...

  9. C#和JS交互 WebBrowser实例

    本文实现了WebBrowser的简单例子 1.引用System.Windows.Froms.dll 2.引用WindowsFormsIntegration.dll 代码如下: public parti ...

  10. 解决IE6、IE7、Firefox兼容最简单的CSS Hack

    写三句代码来控制一个属性,区别Firefox,IE7,IE6: background:orange; *background:green !important; *background:blue;   ...