建议大家还是不要阅读此文了,因为我觉得这题我的解法实在是又不高效又不优美……只是想要记录一下,毕竟是除了中国象棋之外自己做出的组合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的键盘的更多相关文章

  1. [CQOI2017]老C的键盘

    [CQOI2017]老C的键盘 题目描述 额,网上题解好像都是用的一大堆组合数,然而我懒得推公式. 设\(f[i][j]\)表示以\(i\)为根,且\(i\)的权值为\(j\)的方案数. 转移: \[ ...

  2. [BZOJ4824][Cqoi2017]老C的键盘 树形dp+组合数

    4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 218  Solved: 171[Submit][Statu ...

  3. [BZOJ4824][CQOI2017]老C的键盘(树形DP)

    4824: [Cqoi2017]老C的键盘 Time Limit: 10 Sec  Memory Limit: 512 MBSubmit: 193  Solved: 149[Submit][Statu ...

  4. [bzoj4824][洛谷P3757][Cqoi2017]老C的键盘

    Description 老 C 是个程序员. 作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 Q 也 ...

  5. Luogu P3757 [CQOI2017]老C的键盘

    题目描述 老C的键盘 题解 显然对于每个数 x 都有唯一对应的 \(x/2\) , 然而对于每个数 x 却可以成为 \(x*2\) 和 \(x*2+1\) 的对应数 根据这一特性想到了啥??? 感谢l ...

  6. bzoj 4824: [Cqoi2017]老C的键盘

    Description 老 C 是个程序员.     作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序 在某种神奇力量的驱使之下跑得非常快.小 ...

  7. [bzoj4824][Cqoi2017]老C的键盘

    来自FallDream的博客,未经允许,请勿转载,谢谢. 老 C 是个程序员.     作为一个优秀的程序员,老 C 拥有一个别具一格的键盘,据说这样可以大幅提升写程序的速度,还能让写出来的程序在某种 ...

  8. 洛谷 P3757 [CQOI2017]老C的键盘

    题面 luogu 题解 其实就是一颗二叉树 我们假设左儿子小于根,右儿子大于根 考虑树形\(dp\) \(f[u][i]\)表示以\(u\)为根的子树,\(u\)为第\(i\)小 那么考虑子树合并 其 ...

  9. 【BZOJ3167/4824】[Heoi2013]Sao/[Cqoi2017]老C的键盘

    [BZOJ3167][Heoi2013]Sao Description WelcometoSAO(StrangeandAbnormalOnline).这是一个VRMMORPG,含有n个关卡.但是,挑战 ...

随机推荐

  1. 汇编:模拟C语言实现break与continue

    ;=============================== ;循环程序设计 ;模拟C语言实现break 与continue DATAS SEGMENT i dw sum dw DATAS end ...

  2. 【TCP/IP实现磁盘资源的分享-----ISCSI(互联网最小应用程序接口)】

    Iscsi server: 首先把多块磁盘合并为RAID5,便于后期iscis client访问以及服务端的管理 安装 targted服务端包,以及targtedcli创建iscsi TCP/IP共享 ...

  3. DLX算法一览

    目录: 1 X思想的了解. 链表的递归与回溯. 具体操作. 优化. 一些应用与应用中的再次优化(例题). 练手题 X思想的了解. 首先了解DLX是什么? DLX是一种多元未饱和型指令集结构,DLX 代 ...

  4. uva 509 RAID!(磁盘数据)

    来自 https://blog.csdn.net/su_cicada/article/details/80085318 习题4-7 RAID技术(RAID!, ACM/ICPC World Final ...

  5. 各种数据库分页语句整理以及Oracle数据库中的ROWNUM和ORDER BY的区别

    .oracle数据库分页 select * from (select a.*,rownum rc from 表名 where rownum<=endrow) a where a.rc>=s ...

  6. Android面试收集录 Android系统的资源+其他

    1.Android应用程序的资源是如何存储的,如何使用? res文件夹或者assets文件夹 res目录中的资源在R类中生成一个int变量,然后再布局文件中可以直接使用,在代码中,要getResour ...

  7. 隐藏WPF ToolBar 左侧的移动虚线和右侧的小箭头

    原文:隐藏WPF ToolBar 左侧的移动虚线和右侧的小箭头   上面的图是两个工具栏的链接处.   去除蓝色部分的方法是 设置工具栏的ToolBarTray.IsLocked附加选项为True   ...

  8. struts2官方 中文教程 系列十三:利用通配符选择方法

    介绍 在本教程中,我们将介绍如何在struts.xml中配置action节点以达到仅使用一个action节点将几个不同的url关联到特定action类的特定方法.这样做的目的是减少struts.xml ...

  9. 2018春季校园招聘笔经面经合集:Java开发岗

    2018春季校园招聘笔经面经合集:Java开发岗 以下为精选面经: 美团外卖配送部后台开发面经 nowcoder.com/discuss/76 春招总结,干货满满 nowcoder.com/discu ...

  10. 线段树简单入门 (含普通线段树, zkw线段树, 主席树)

    线段树简单入门 递归版线段树 线段树的定义 线段树, 顾名思义, 就是每个节点表示一个区间. 线段树通常维护一些区间的值, 例如区间和. 比如, 上图 \([2, 5]\) 区间的和, 为以下区间的和 ...