「6月雅礼集训 2017 Day10」quote
【题目大意】
一个合法的引号序列是空串;如果引号序列合法,那么在两边加上同一个引号也合法;或是把两个合法的引号序列拼起来也是合法的。
求长度为$n$,字符集大小为$k$的合法引号序列的个数。多组数据。
$1 \leq T \leq 10^5, 1 \leq n \leq 10^7, 1\leq k \leq 10^9$
【题解】
显然引号序列可以看做括号序列,于是我们有了一个$O(n^2)$的dp了。
设$f_{i,j}$表示到第$i$个位置,前面有$j$个左引号没有匹配,的方案数
每次,要么有1种方案匹配前面的某一个引号,要么有$(k-1)$种方案开启一个新的左引号。
特别地,当$j=0$的时候,只能开启新的左引号,有$k$种方案。
就是当$j\geq 1$时:
$f_{i+1,j+1} = f_{i+1,j+1} + (k-1)f_{i,j}$
$f_{i+1,j-1} = f_{i+1,j-1} + f_{i,j}$
特别地,当$j=1$时:
$f_{i+1,j+1} = f_{i+1,j+1} + kf_{i,j}$
于是这是一个优秀的$O(n^2)$做法。
考虑如何优化,这里我们不讨论关于生成函数、暴力解方程等方法。
生成函数 大力化简 详见 https://chrt.github.io/2017/07/04/oeis-a183135/
考虑一种优秀的做法:
转化模型:等价于,我要在数轴上从0开始走$n$步,每次可以向正方向走、向负方向走,不能走到负半轴。当不在原点的时候,向正方向有$(k-1)$种方法,向负方向有1种方法;在原点的时候,向正方向有$k$种方法。最后回到原点的方案数。
由于$k = (k-1) + 1$,我们就可以把那种往上的方案对应成往下的,有如下转化:
我们定义一线表示实际的括号序列;二线表示每个一线对应的另外一种括号序列,见下。
比如一线的括号序列是左括号+1,右括号-1的折线;二线的括号序列就是右括号-1,左括号在0的时候-1,其他+1的折线。
更通俗的说,一线的括号序列是正常的一个括号序列;二线的括号序列是把一线括号序列中,每次从0开始,选择$k$种方法中的一种,走到1的这个左括号,人为看做右括号(因为它并没有贡献$(k-1)$的方案)。
那么二线对应着一种一线的括号序列。二线的左括号个数$i$,也就是实际需要乘$(k-1)$的个数(由于其他的左括号,是因为碰到了0,在$k$种方案中有1种方案向上,我们选择了那种方案导致)。
考虑有多少种二线有$i$个左括号的括号序列,对应到一线中是合法的。
二线有$i$个左括号,那么二线的终止位置是$-2(n-i)$;如果二线走到了$-2(n-i)-1$,相当于我一线从0用$k$种方法的1种方法,走到了1,这个一线实际上对应的二线方案应该只有$i-1$个左括号(因为这个左括号是没有用的,不应该被乘$(k-1)$)。
好需要证明每个走到$-2(n-i)-1$的二线有$i$个括号的方案和二线有$i-1$个括号的方案是一一对应的。这个其实显然,我走到了$-2(n-i)-1$的点后,把后面的括号翻转,就对应于一个有$i-1$的括号序列了。类似于卡特兰数的证明。
所以总的方案就是${2n\choose i} - {2n\choose i-1}$。
我们特殊定义${x \choose -1} = 0$。
然后答案就是$\sum_{i=0}^{n} ({2n\choose i} - {2n\choose i-1})(k-1)^i$
首先减法可以分开处理,我们只要处理$\sum_{i=0}^{n}{2n\choose i}(k-1)^i$的线性递推问题即可。
这个我们可以用广义杨辉三角形来解决线性递推问题。
考虑杨辉三角形的构造
相当于把上一行复制一遍,(乘上对应系数1),移到下一行,右移一位,两两相加。
e.g

那么这个给我们创造了一个非常好的线性递推的思路,我们可以将上一行乘以$(k-1)$,移到下一行,右移一位,两两相加。
正确性非常明显,上一行第$i$个数,乘了$(k-1)$,相当于第$i+1$列,也就是下一行的第$i+1$个数。
然后我们发现我们求得是每两行的广义杨辉三角的前一半的和。
我们从偶数行的一半乘以$(k-1)+1$(包括自己,因为上面是一行+另一行乘$(k-1)$),减去边界,推到奇数行;
再从奇数行的一半乘以$(k-1)+1$,减去边界,推到偶数行,注意边界问题。
最好是自己模拟下k=3的情况,然后就能知道边界是什么了。
注意0的情况答案是1.
可能卡卡常就过了?
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm> using namespace std; typedef long long ll;
typedef unsigned long long ull;
typedef long double ld; const int N = 1e7 + , M = 2e7 + , F = 1e7, FM = 2e7;
const int mod = 1e9 + ; int n, K, A;
int f[N], g[N], p[N], c[N];
int fac[M], inv[M];
inline int pwr(int a, int b) {
int ret = ;
while(b) {
if(b&) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
b >>= ;
}
return ret;
} inline int C(int n, int k) {
if(n < k) return ;
return 1ll * fac[n] * inv[k] % mod * inv[n-k] % mod;
} int main() {
// freopen("quote.in", "r", stdin);
// freopen("quote.out", "w", stdout);
int T; cin >> T >> K; A = K--;
if(!K) {
while(T--) scanf("%d", &n), puts("");
return ;
}
fac[] = inv[] = p[] = ;
for (int i=; i<=FM; ++i) fac[i] = 1ll * fac[i-] * i % mod;
inv[FM] = pwr(fac[FM], mod-);
for (int i=FM-; i; --i) inv[i] = 1ll * inv[i+] * (i+) % mod;
for (int i=; i<=F; ++i) p[i] = 1ll * p[i-] * K % mod;
f[] = K + K + ; if(f[] >= mod) f[] -= mod;
for (int i=, t; i<=F; ++i) {
t = 1ll * A * f[i-] % mod;
t = t + 1ll * C(i*-, i) * p[i] % mod;
if(t >= mod) t -= mod;
t = 1ll * A * t % mod;
t = t - 1ll * K * C(i*-, i) % mod * p[i] % mod;
if(t < ) t += mod;
f[i] = t;
} for (int i=, t; i<=F; ++i) {
t = f[i] - 1ll * C(i*, i) * p[i] % mod;
g[i] = 1ll * t * K % mod;
if(g[i] < ) g[i] += mod;
f[i] -= g[i];
if(f[i] < ) f[i] += mod;
} f[] = ;
while(T--) {
scanf("%d", &n);
printf("%d\n", f[n]);
}
return ;
}
upd: 本题卡常技巧
减少取模次数,数组大可以开static(迷)
upd2: 卡了波常,然后过了
# include <stdio.h>
# include <string.h>
# include <iostream>
# include <algorithm> using namespace std; typedef long long ll;
typedef unsigned long long ull;
typedef long double ld; const int N = 1e7 + , M = 2e7 + , F = 1e7, FM = 2e7;
const int mod = 1e9 + ; inline int getint() {
int x = ; char ch = getchar();
while(!isdigit(ch)) ch = getchar();
while(isdigit(ch)) x = (x<<) + (x<<) + ch - '', ch = getchar();
return x;
} int n, K, A;
int fac[M], inv[M], p[N], c[N];
inline int pwr(int a, int b) {
int ret = ;
while(b) {
if(b&) ret = 1ll * ret * a % mod;
a = 1ll * a * a % mod;
b >>= ;
}
return ret;
} inline int C(int n, int k) {
if(n < k) return ;
return 1ll * fac[n] * inv[k] % mod * inv[n-k] % mod;
} int main() {
// freopen("quote.in", "r", stdin);
// freopen("quote.out", "w", stdout);
static int f[N], g[N];
int T; T = getint(); K = getint(); A = K--;
if(!K) {
while(T--) puts("");
return ;
}
fac[] = inv[] = p[] = ;
for (int i=; i<=FM; ++i) fac[i] = 1ll * fac[i-] * i % mod;
inv[FM] = pwr(fac[FM], mod-);
for (int i=FM-; i; --i) inv[i] = 1ll * inv[i+] * (i+) % mod;
for (int i=; i<=F; ++i) p[i] = 1ll * p[i-] * K % mod;
f[] = K + K + ; if(f[] >= mod) f[] -= mod;
for (int i=, t; i<=F; ++i) {
t = (1ll * A * f[i-] + 1ll * C(i*-, i) * p[i]) % mod;
t = (1ll * A * t - 1ll * K * C(i*-, i) % mod * p[i]) % mod;
if(t < ) t += mod; f[i] = t;
}
for (int i=, t; i<=F; ++i) {
t = f[i] - 1ll * C(i*, i) * p[i] % mod;
g[i] = 1ll * t * K % mod;
if(g[i] < ) g[i] += mod;
f[i] -= g[i];
if(f[i] < ) f[i] += mod;
}
f[] = ;
while(T--) {
n = getint();
printf("%d\n", f[n]);
}
return ;
}


「6月雅礼集训 2017 Day10」quote的更多相关文章
- 「6月雅礼集训 2017 Day10」perm(CodeForces 698F)
[题目大意] 给出一个$n$个数的序列$\{a_n\}$,其中有些地方的数为0,要求你把这个序列填成一个1到$n$的排列,使得: $(a_i, a_j) = 1$,当且仅当$(i, j) = 1$.多 ...
- 「6月雅礼集训 2017 Day4」qyh(bzoj2687 交与并)
原题传送门:http://www.lydsy.com/JudgeOnline/problem.php?id=2687 [题目大意] 给出若干区间,求一个区间的大于等于2的子集,使得 |区间并| 和 | ...
- 「6月雅礼集训 2017 Day11」delight
[题目大意] 有$n$天,每天能吃饭.睡觉.什么事也不干 每天吃饭的愉悦值为$e_i$,睡觉的愉悦值为$s_i$,什么都不干愉悦值为0. 要求每连续$k$天都要有至少$E$天吃饭,$S$天睡觉. 求最 ...
- 「6月雅礼集训 2017 Day11」jump
[题目大意] 有$n$个位置,每个位置有一个数$x_i$,代表从$i$经过1步可以到达的点在$[\max(1, i-x_i), \min(i+x_i, n)]$中. 定义$(i,j)$的距离表示从$i ...
- 「6月雅礼集训 2017 Day11」tree
[题目大意] 给出一棵带权树,有两类点,一类黑点,一类白点. 求切断黑点和白点间路径的最小代价. $n \leq 10^5$ [题解] 直接最小割能过..但是树形dp明显更好写 设$f_{x,0/1/ ...
- 「6月雅礼集训 2017 Day8」route
[题目大意] 给出平面上$n$个点,求一条连接$n$个点的不相交的路径,使得转换的方向符合所给长度为$n-2$的字符串. $n \leq 5000$ [题解] 考虑取凸包上一点,然后如果下一个是‘R' ...
- 「6月雅礼集训 2017 Day8」gcd
[题目大意] 定义times(a, b)表示用辗转相除计算a和b的最大公约数所需步骤. 那么有: 1. times(a, b) = times(b, a) 2. times(a, 0) = 0 3. ...
- 「6月雅礼集训 2017 Day8」infection
[题目大意] 有$n$个人,每个人有一个初始位置$x_i$和一个速度$v_i$,你需要选择若干个人来感染一个傻逼病毒. 当两个人相遇(可以是正面和背面),傻逼病毒会传染,求经过无限大时间后,传染完所有 ...
- 「6月雅礼集训 2017 Day7」电报
[题目大意] 有n个岛屿,第i个岛屿有有向发射站到第$p_i$个岛屿,改变到任意其他岛屿需要花费$c_i$的代价,求使得所有岛屿直接或间接联通的最小代价. $1 \leq n \leq 10^5, 1 ...
随机推荐
- CentOs7.3 搭建 Redis-4.0.1 Cluster 集群服务
环境 VMware版本号:12.0.0 CentOS版本:CentOS 7.3.1611 三台虚拟机(IP):192.168.252.101,192.168.102..102,192.168.252. ...
- Agile.Net 组件式开发平台 - 驱动开发示例
首先讲一下概念,此驱动非彼驱动.在Agle.Net中我们将组件规划成两种类型,一种是基于业务的窗体组件,一种是提供扩展功能的驱动组件. 打个比方例如一般系统中需要提供身份证读卡功能,然而市面上有很多种 ...
- QT分析之QPushButton的初始化
原文地址:http://blog.163.com/net_worm/blog/static/127702419201001003326522/ 在简单的QT程序的第二行,声明了一个QPushButto ...
- Android基础------SQLite数据库(二)
1.操作SQLite数据库 1.1 execSQL() 可以执行insert.delete.update和CREATE TABLE之类有更改行为的SQL语句 1.2 rawQuery() 可以执行se ...
- change object keys & UpperCase & LowerCase
change object keys & UpperCase & LowerCase .toLocaleUpperCase(); && .toLocaleLowerCa ...
- Delphi SQL语句字符串拼接
单引号必须成对出现,最外层的单引号表示其内部符号为字符:除最外层以外的单引号,每两个单引号代表一个'字符.加号:+用于字符串之间的连接.字符串常量用四个单引号,例如 ' select * from T ...
- asp.net 间隔一段时间执行某方法
设想网站后台每秒自动更新一下Cache["test"]中的值,通过这个实现就可以完成一些在间隔多少时间更新一下数据库的操作. 1.定义一个事件类BMAEvent,在Processo ...
- 关联容器 // append方法
关联容器和顺序容器的差别在于:关联容器通过键(key)存储和读取元素,而顺序容器则通过元素在容器中的位置顺序存储和访问元素. 1.关联容器支持通过键来高效地查找和读取元素.两个基本的关联容器类型是ma ...
- 【以前的空间】poj 2288 Islands and Bridges
一个不错的题解 : http://blog.csdn.net/accry/article/details/6607703 这是一道状态压缩.每个点有一个值,我们最后要求一个最值sum.sum由三部分组 ...
- HDU5726:GCD——题解
题目:hdu的5726 (我原博客的东西,正好整理过来,属于st表裸题) (可以看出我当时有多么的菜--) 这道题写了一遍,然而蒟蒻的我的时间爆炸了-- 于是看了一下学长的代码(顺便在此处%一下学长) ...