【题解】CQOI2017老C的键盘
建议大家还是不要阅读此文了,因为我觉得这题我的解法实在是又不高效又不优美……只是想要记录一下,毕竟是除了中国象棋之外自己做出的组合dp第一题~
首先如果做题做得多,比较熟练的话,应该能一眼看出这题所给的信息正好描述的是一棵二叉树上父子的大小关系。于是确立一个状态 \(f[u][i]\) 表示在 \(u\) 及 \(u\) 的子树内 \(u\) 排名第 i 名的总方案数。(这个状态应该还是比较好想,我当时想到这个状态觉得是可做的就坚持了这个状态)。那么就考虑如何通过 \(f[ch1][j], f[ch2][k]\) 即它的两个儿子来转移到当前的状态。
我们可以注意到:由儿子转移到父亲,两个儿子之间其实是没有互相关联的,只要让它们都满足与父亲的大小限制即可。当排名为 \(j\) 的儿子小与父亲的时候,说明父亲前面起码预留出 \(j\) 个空位,而当排名为 \(j\) 的儿子大于父亲时,说明父亲的排名也不能超过限制使得后面不足 \(size2 - j + 1\) 个空位。设 \(S\) 为右儿子子树中排名 \( < i \) 的个数,则根据这个思路,就可以得出 \(S\) 的大小范围。
于是我们有组合数转移方程 \(ans = f[ch1][j] * f[ch2][k] * C[i - 1][S] * C[size[u] - i][size[ch2] - S]\) ;
代码又臭又长,不忍直视……
#include <bits/stdc++.h>
using namespace std;
#define maxn 400
#define int long long
#define mod 1000000007
int n, f[maxn][maxn], size[maxn];
int C[maxn][maxn], a[maxn]; int read()
{
int x = , k = ;
char c;
c = getchar();
while(c < '' || c > '') { if(c == '-') k = -; c = getchar(); }
while(c >= '' && c <= '') x = x * + c - '', c = getchar();
return x * k;
} void Get_C()
{
for(int i = ;i < maxn; i++)
C[i][] = , C[i][i] = ;
for(int i = ; i < maxn; i++)
for(int j = ; j < i; j++)
C[i][j] = (C[i - ][j - ] + C[i - ][j]) % mod;
} void DP(int u)
{
int ch1 = u * , ch2 = u * + ;
for(int i = ; i <= size[u]; i ++)
for(int j = ; j <= size[ch1]; j ++)
for(int k = ; k <= size[ch2]; k ++)
{
int tem = f[ch1][j] * f[ch2][k] % mod;
int l1 = size[ch1] - j + , l2 = size[ch2] - k + ;
if(a[ch1] && a[ch2])
{
if(i <= j + k) continue;
int minn = max(k, max(size[ch2] - (size[u] - i), i - size[ch1] - ));
int maxx = min(i - j - , size[ch2]);
for(int S = minn; S <= maxx; S ++)
{
int tt = tem * C[i - ][S] % mod * C[size[u] - i][size[ch2] - S] % mod;
f[u][i] = (f[u][i] + tt) % mod;
}
}
else if(a[ch1] || a[ch2])
{
int x = j, y = k, p = ch1, q = ch2;
if(a[ch2]) swap(j, k), swap(ch1, ch2);
int l1 = size[ch1] - j + , l2 = size[ch2] - k + ;
if(i <= j || i > size[u] - l2) { j = x, k = y, ch1 = p, ch2 = q; continue; }
int minn = max(i - - size[ch1], 0ll);
int maxx = min(i - - j, size[ch2] - l2);
for(int S = minn; S <= maxx; S ++)
{
int tt = tem * C[i - ][S] % mod * C[size[u] - i][size[ch2] - S] % mod;
f[u][i] = (f[u][i] + tt) % mod;
}
j = x, k = y, ch1 = p, ch2 = q;
}
else
{
int z = size[u] - i;
if(i > size[u] - l1 - l2) continue;
int minn = max(size[ch2] - (z - l1), 0ll);
int maxx = min(size[ch2] - l2, size[u] - z + l2 - );
for(int S = minn; S <= maxx; S ++)
{
int tt = tem * C[i - ][S] % mod * C[size[u] - i][size[ch2] - S] % mod;
f[u][i] = (f[u][i] + tt) % mod;
}
}
}
} void dfs(int u)
{
if(u > n) return;
dfs(u * ); dfs(u * + );
if(u * > n && u * + > n)
{
f[u][] = ; size[u] = ;
return;
}
int ch1 = u * , ch2 = u * + ;
size[u] = size[ch1] + size[ch2] + ;
if(ch2 <= n) DP(u);
else
{
for(int i = ; i <= size[u]; i ++)
for(int j = ; j <= size[ch1]; j ++)
{
if(a[ch1])
{
if(j >= i) continue;
f[u][i] = (f[u][i] + f[ch1][j]) % mod;
}
else
{
int p = size[ch1] - j + , z = size[u] - i;
if(z < p) continue;
f[u][i] = (f[u][i] + f[ch1][j]) % mod;
}
}
}
} signed main()
{
Get_C();
n = read();
for(int i = ; i <= n; i ++)
{
char c; cin >> c;
if(c == '>') a[i] = ;
}
dfs(); int ans = ;
for(int i = ; i <= n; i ++)
ans = (ans + f[][i]) % mod;
printf("%lld\n", ans);
return ;
}
【题解】CQOI2017老C的键盘的更多相关文章
- [CQOI2017]老C的键盘
[CQOI2017]老C的键盘 题目描述 额,网上题解好像都是用的一大堆组合数,然而我懒得推公式. 设\(f[i][j]\)表示以\(i\)为根,且\(i\)的权值为\(j\)的方案数. 转移: \[ ...
- [BZOJ4824][Cqoi2017]老C的键盘 树形dp+组合数
4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 218 Solved: 171[Submit][Statu ...
- [BZOJ4824][CQOI2017]老C的键盘(树形DP)
4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec Memory Limit: 512 MBSubmit: 193 Solved: 149[Submit][Statu ...
- [bzoj4824][洛谷P3757][Cqoi2017]老C的键盘
Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 Q 也 ...
- Luogu P3757 [CQOI2017]老C的键盘
题目描述 老C的键盘 题解 显然对于每个数 x 都有唯一对应的 \(x/2\) , 然而对于每个数 x 却可以成为 \(x*2\) 和 \(x*2+1\) 的对应数 根据这一特性想到了啥??? 感谢l ...
- bzoj 4824: [Cqoi2017]老C的键盘
Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 ...
- [bzoj4824][Cqoi2017]老C的键盘
来自FallDream的博客,未经允许,请勿转载,谢谢. 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序在某种 ...
- 洛谷 P3757 [CQOI2017]老C的键盘
题面 luogu 题解 其实就是一颗二叉树 我们假设左儿子小于根,右儿子大于根 考虑树形\(dp\) \(f[u][i]\)表示以\(u\)为根的子树,\(u\)为第\(i\)小 那么考虑子树合并 其 ...
- 【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘
[BZOJ3167][Heoi2013]Sao Description WelcometoSAO(StrangeandAbnormalOnline).这是一个VRMMORPG,含有n个关卡.但是,挑战 ...
随机推荐
- mysql中tinyint、smallint、mediumint,int 和bigint 的区别
1:字节的定义:字节(Byte):字节是通过网络传输信息(或在硬盘或内存中存储信息)的单位. 一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间 2.在mysql中一个汉字占三个 ...
- mysql 5.7 配置初始化及修改 ROOT 用户密码
1.修改配置文件 my.ini 放在 mysql\bin [mysqld] basedir=C:\Mysql datadir=C:\Mysql\data port=3306 # server_id = ...
- (数据科学学习手札23)决策树分类原理详解&Python与R实现
作为机器学习中可解释性非常好的一种算法,决策树(Decision Tree)是在已知各种情况发生概率的基础上,通过构成决策树来求取净现值的期望值大于等于零的概率,评价项目风险,判断其可行性的决策分析方 ...
- 最小生成树算法 1.Prim算法
最小生成树(MST):一个有N个点的图,边一定是大于等于N-1条边的.在这些边中选择N-1条出来,连接所有N个点.这N-1条边的边权之和是所有方案中最小的. Prim算法的时间复杂度时O(n^2)的, ...
- 【Leetcode】Jewels and Stones
Jewels and Stones Description You're given strings J representing the types of stones that are jewel ...
- Java基础——内部类
一.什么是内部类 将一个类定义在另一个类里面或者一个方法里面,这样的类称为内部类 内部类所在的类在编译成功后,会出现这样两个class文件:OuterClass.class和OuterClass$In ...
- 关于springboot 打包问题 jar包和 war包
起因:项目开发完成 需要打包部署了 发现自己不会打包 那么开始网上学习打包? 那么怎么来打包那? 我们以前没有采用springboot 时候我们都是直接将项目打成war包形式 然后放到tomc ...
- [网站日志]今天早上遭遇的CPU 100%情况
今天早上9:06左右,Windows性能监视器监测到主站的Web服务器出现了CPU 100%的情况,伴随着Requests/Sec的上升,详见下图. 上图中红色线条表示的是%Processor Tim ...
- html基础问题总结
1.reflow 在CSS规范中有一个渲染对象的概念,通常用一个盒子(box, rectangle)来表示.mozilla通过一个叫frame的对象对盒子进行操作.frame主要的动作有三个: 构造f ...
- Lambda表达式在Kotlin中怎样工作的:setOnClickListener的转换(KAD 18)
作者:Antonio Leiva 时间:Mar 28, 2017 原文链接:https://antonioleiva.com/lambdas-kotlin-android/ 虽然,我在其它文章讲过一点 ...