@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. 用localStorage在页面间传值

    注意:要在同一域名下的页面才有效 在需要存储数据页面用localStorage设置数据 localStorage.setItem(key,value);//key要用单引号或者双引号包括着,value ...

  2. swing界面jframe新开线程自动定时刷新

    https://www.cnblogs.com/softidea/p/4411452.html

  3. python基础--类的继承以及mro

    继承: 什么是继承: 继承是一种关系,描述两个对象之间什么是什么的关系 在程序中,继承描述的是类和类之间的关系 例如 a继承了b,a就能直接使用b已经存在的方法和属性了 a称之为子类,b称之为父类,成 ...

  4. hystrix熔断器

    在一个具有多服务的应用中,假如由于其中某一个服务出现问题,导致响应速度变慢,或是根本没有响应返回,会导致它的服务消费者由于长时间的等待,消耗尽线程,进而影响到对其他服务的线程调用,进而会转变为整个应用 ...

  5. java-继承进阶_抽象类_接口

    概要图 一, 继承的进阶 1.1,成员变量 重点明确原理. 特殊情况: 子父类中定义了一模一样的成员变量. 都存在于子类对象中. 如何在子类中直接访问同名的父类中的变量呢? 通过关键字 super来完 ...

  6. 洛谷P2429 制杖题 [2017年6月计划 数论10]

    P2429 制杖题 题目描述 求不大于 m 的. 质因数集与给定质数集有交集的自然数之和. 输入输出格式 输入格式: 第一行二个整数 n,m. 第二行 n 个整数,表示质数集内的元素 p[i]. 输出 ...

  7. LUGOU 3959 宝藏 (noip 2017 day2 T2)

    传送门 解题思路 去年noip现在拿来写..思路还是听清楚的,记忆化搜索,f[S]表示现在选了集合S时的最小代价,dis[i]表示达到最优时i这个点的深度.f[S| (1< < i-1) ...

  8. LintCode刷题笔记-- Distinct Subsequences

    标签:动态规划 题目描述: Given a string S and a string T, count the number of distinct subsequences of T in S. ...

  9. No.6 Verilog 其他论题

    (1)任务  **任务类似于一段程序,可以提供一种能力,使设计者可以从设计描述的不同位置执行共同的代码段.任务可以包含时序控制, 可以调用其它任务和函数.  任务的定义格式: task[automat ...

  10. 在vue项目中同时使用element-ui和mint-ui,的时候,.babelrc配置文件怎么写

    我们安装vue组件库的时候,考虑到大小问题,需要根据需要仅引入部分组件 借助 babel-plugin-component,我们可以只引入需要的组件,以达到减小项目体积的目的. 但是在配置  .bab ...