博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
【bzoj5161】最长上升子序列 状压dp+打表
阅读量:4967 次
发布时间:2019-06-12

本文共 2240 字,大约阅读时间需要 7 分钟。

题目描述

现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望。
为了避免精度误差,你只需要输出答案模998244353的余数。

输入

输入只包含一个正整数n。N<=28

输出

输出只包含一个非负整数,表示答案模998244353的余数。
可以证明,答案一定为有理数,设其为a/b(a、b为互质的整数),你输出的整数为x,
则你需要保证0≤x<998244353且a与bx模998244353同余。

样例输入

2

样例输出

499122178


题解

状压dp+打表

套路:对于排列问题,从左到右处理比较困难的话,考虑从小到大把数插入来处理。

对于一个确定的 $1\sim n$ 的排列,令 $f[i]$ 表示该排列以第 $i$ 个数结尾的最长上升子序列长度。令 $mx[i]$ 表示其前缀最大值,显然 $mx[i]\le mx[i+1]\le mx[i]+1$ ,根据这个我们可以状压前缀最大值的差分数组。

考虑在 $i$ 位置和 $i+1$ 位置加入一个新的最大数:这个数结尾的最长上升子序列长度一定为 $mx[i]+1$ ,因此把该位改成1,这个数后面的第一个1受其影响差分数组-1,把它改成0。

设 $dp[i][j]$ 表示 $1\sim i$ 的排列,$mx$ 的差分数组状压后为 $j$ 的方案数。那么答案就是 $\sum 每种状态的数目\times 每种状态的最长上升子序列长度$ 。

代码中我没有状压 $mx[1]-mx[0]$ ,因为一定是 $1$ 。这样答案就是 $\sum\limits_{i=0}^{2^{n-1}-1}dp[n][i]·cnt[i]$ ,$cnt[i]$ 表示 $i$ 种 $1$ 的数目。

最后乘以阶乘的逆元即为期望。

时间复杂度 $O(n^2·2^n)$ ,过不去。怎么办?打表...

打表程序:

#include 
#include
#define mod 998244353typedef long long ll;int f[2][134217735] , cnt[134217735];ll pow(ll x , int y){ ll ans = 1; while(y) { if(y & 1) ans = ans * x % mod; x = x * x % mod , y >>= 1; } return ans;}int main(){ int n , i , j , k , d , t , pos; ll ans = 0 , fac = 1; scanf("%d" , &n) , n -- ; f[0][0] = 1; for(d = i = 1 ; i <= n ; i ++ , d ^= 1) { memset(f[d] , 0 , sizeof(int) * (1 << i)); for(j = 0 ; j < (1 << (i - 1)) ; j ++ ) { f[d][j << 1] = (f[d][j << 1] + f[d ^ 1][j]) % mod , pos = -1; for(k = i - 1 ; ~k ; k -- ) { t = ((j >> k) << (k + 1)) | (1 << k) | (j & ((1 << k) - 1)); if(j & (1 << k)) pos = k; if(~pos) t ^= (1 << (pos + 1)); f[d][t] = (f[d][t] + f[d ^ 1][j]) % mod; } } } for(i = 1 ; i < (1 << n) ; i ++ ) cnt[i] = cnt[i - (i & -i)] + 1; for(i = 0 ; i < (1 << n) ; i ++ ) ans = (ans + 1ll * f[n & 1][i] * (cnt[i] + 1)) % mod; for(i = 1 ; i <= n + 1 ; i ++ ) fac = fac * i % mod; printf("%lld\n" , ans * pow(fac , mod - 2) % mod); return 0;}

AC程序:

#include 
int a[]={1,499122178,2,915057326,540715694,946945688,422867403,451091574,317868537,200489273, 976705134,705376344,662845575,331522185,228644314,262819964,686801362,495111839,947040129,414835038,696340671,749077581,301075008,314644758,102117126,819818153,273498600,267588741},n;int main(){ scanf("%d",&n); printf("%d",a[n-1]); return 0;}

 

转载于:https://www.cnblogs.com/GXZlegend/p/8619335.html

你可能感兴趣的文章
每次阅读外文技术资料都头疼,终于知道原因了。
查看>>
zabbix短信网关调用问题总结
查看>>
130242014034-林伟领-实验一
查看>>
Forbidden You don't have permission to access / on this server.
查看>>
Windows server 2008 R2中安装MySQL !
查看>>
Intellij Idea新建web项目(转)
查看>>
raspberry 安装apache2,使其支持ssl ,并创建自签名证书
查看>>
Trie树:应用于统计和排序
查看>>
C语言结构体和函数
查看>>
[BZOJ3449] [Usaco2014 Feb]Secret Code
查看>>
XHTML与HTML区别
查看>>
软考-程序设计语言基础(编译原理)
查看>>
2016峰会:项目管理与高级项目管理(广州站)
查看>>
用JAVA编写浏览器内核之实现javascript的document对象与内置方法
查看>>
linux 命令之top
查看>>
有关远程设置的问题
查看>>
BZOJ 1800: [Ahoi2009]fly 飞行棋
查看>>
2019,2月份第三个星期,js小突破了一波,笔记
查看>>
洛谷 [P3033] 牛的障碍
查看>>
jquery 对HTML标签的克隆、删除
查看>>