bzoj5161 最长上升子序列 状压DP(DP 套 DP) + 打表
题目传送门
https://lydsy.com/JudgeOnline/problem.php?id=5161
题解
回顾一下以前用二分求 LIS 的方法:令 \(f[i]\) 表示长度为 \(i\) 的 LIS 的最后一位的最小值。可发现不管目前 DP 到了哪儿,这个东西永远是递增的。
关于 LIS 的题目的大都可以维护一些和这个东西有关的状态,所以我们考虑状压这个数组。因为这个数组中每一位都不重复,所以可以用 \(01\) 来状压成一个二进制数。
由于我们在转移状态的时候,不关系新来的数到底是几,只关心这个新来的数和之前的数的大小关系,所以我们可以修改之前的 \(f\) 的定义为最后一位是已经 DP 过的数中第几小的数。
这样,新插入一个数的时候,可以枚举它的大小的排名,然后替换掉它后一名的数,并把后面的数全部 \(+1\),这个可以通过二进制数的移位实现。
最后 \(dp[n][2^n - 1]\) 就是答案。
因为在第 \(i\) 位的时候,状态只需要枚举到 \(2^i\),所以总的复杂度为 \(O(n(2^0 + 2^1 + 2^2 + \cdots)) = O(n2^n)\)。
但是 \(O(n2^n)\) 在 \(n \leq 28\) 面前显得苍白无力。怎么办呢?
\(n = 28\) 的时候 \(28 \times 2^{28}\) 大概是 \(7e9\) 左右,一分钟以内可以跑完。所以,可以花一点出去 orz 别的神仙的时间(大概 \(2-3min\))来打一个表。
然后就可以通过这道题了。
正常代码
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
#define lowbit(x) ((x) & -(x))
const int N = 28 + 7;
const int M = (1 << 27) + 7;
const int P = 998244353;
int n, S;
int dp[2][M], pcnt[M], suf[N];
inline int smod(int x) { return x >= P ? x - P : x; }
inline void sadd(int &x, const int &y) { x += y; x >= P ? x -= P : x; }
inline int fpow(int x, int y) {
int ans = 1;
for (; y; y >>= 1, x = (ll)x * x % P) if (y & 1) ans = (ll)ans * x % P;
return ans;
}
inline void ycl() {
S = (1 << (n - 1)) - 1;
for (int s = 1; s <= S; ++s) pcnt[s] = pcnt[s ^ lowbit(s)] + 1;
}
inline void work() {
ycl();
int now = 1, pp = 0;
dp[now][0] = 1;
for (int i = 1; i < n; ++i) {
std::swap(now, pp);
for (int s = 0; s < (1 << i); ++s) dp[now][s] = 0;
for (int sss = 0; sss < (1 << (i - 1)); ++sss) {
int pre = 0, s = sss << 1 | 1;
for (int j = i; j; --j) suf[j] = (s >> (j - 1)) & 1 ? j : suf[j + 1];
for (int j = 0; j <= i; ++j) {
if (j) pre += (s >> (j - 1)) & 1;
int ss, sta, p = suf[j + 1];
if (p) ss = s ^ (1 << (p - 1));
sta = (s & ((1 << j) - 1)) | ((ss & ~((1 << j) - 1)) << 1) | (1 << j);
sadd(dp[now][sta >> 1], dp[pp][sss]);
}
}
}
int ans = 0;
for (int i = 0; i <= S; ++i) sadd(ans, (ll)(pcnt[i] + 1) * dp[now][i] % P);
for (int i = 1; i <= n; ++i) ans = (ll)ans * fpow(i, P - 2) % P;
printf("%d\n", ans);
}
inline void init() {
read(n);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
打表代码
#include<bits/stdc++.h>
#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back
template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}
typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;
template<typename I> inline void read(I &x) {
int f = 0, c;
while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
x = c & 15;
while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
f ? x = -x : 0;
}
#define lowbit(x) ((x) & -(x))
const int N = 28 + 7;
const int M = (1 << 27) + 7;
const int P = 998244353;
const int ans[] = {0, 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};
int n, S;/*
int dp[2][M], pcnt[M], suf[N];
inline int smod(int x) { return x >= P ? x - P : x; }
inline void sadd(int &x, const int &y) { x += y; x >= P ? x -= P : x; }
inline int fpow(int x, int y) {
int ans = 1;
for (; y; y >>= 1, x = (ll)x * x % P) if (y & 1) ans = (ll)ans * x % P;
return ans;
}
inline void ycl() {
S = (1 << (n - 1)) - 1;
for (int s = 1; s <= S; ++s) pcnt[s] = pcnt[s ^ lowbit(s)] + 1;
}
inline void work() {
ycl();
int now = 1, pp = 0;
dp[now][0] = 1;
for (int i = 1; i < n; ++i) {
std::swap(now, pp);
for (int s = 0; s < (1 << i); ++s) dp[now][s] = 0;
for (int sss = 0; sss < (1 << (i - 1)); ++sss) {
int pre = 0, s = sss << 1 | 1;
for (int j = i; j; --j) suf[j] = (s >> (j - 1)) & 1 ? j : suf[j + 1];
for (int j = 0; j <= i; ++j) {
if (j) pre += (s >> (j - 1)) & 1;
int ss, sta, p = suf[j + 1];
if (p) ss = s ^ (1 << (p - 1));
sta = (s & ((1 << j) - 1)) | ((ss & ~((1 << j) - 1)) << 1) | (1 << j);
// dbg("i = %d, s = %d, j = %d, sta = %d, now = %d, pre = %d\n", i, s, j, sta, now, pp);
sadd(dp[now][sta >> 1], dp[pp][sss]);
}
}
}
int ans = 0;
for (int i = 0; i <= S; ++i) sadd(ans, (ll)(pcnt[i] + 1) * dp[now][i] % P);
for (int i = 1; i <= n; ++i) ans = (ll)ans * fpow(i, P - 2) % P;
printf("%d\n", ans);
}*/
inline void work() {
printf("%d\n", ans[n]);
}
inline void init() {
read(n);
}
int main() {
#ifdef hzhkk
freopen("hkk.in", "r", stdin);
#endif
init();
work();
fclose(stdin), fclose(stdout);
return 0;
}
bzoj5161 最长上升子序列 状压DP(DP 套 DP) + 打表的更多相关文章
- 【bzoj5161】最长上升子序列 状压dp+打表
题目描述 现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望. 为了避免精度误差,你只需要输出答案模998244353的余数. 输入 输入只包含一个正整数n.N<=28 输出 输出只包 ...
- BZOJ.3591.最长上升子序列(状压DP)
BZOJ 题意:给出\(1\sim n\)的一个排列的一个最长上升子序列,求原排列可能的种类数. \(n\leq 15\). \(n\)很小,参照HDU 4352这道题,我们直接把求\(LIS\)时的 ...
- BZOJ 5161: 最长上升子序列 状压dp+查分
好神啊 ~ 打表程序: #include <cstdio> #include <cstring> #include <algorithm> #define N 14 ...
- 求解最长递增子序列(LIS) | 动态规划(DP)+ 二分法
1.题目描述 给定数组arr,返回arr的最长递增子序列. 2.举例 arr={2,1,5,3,6,4,8,9,7},返回的最长递增子序列为{1,3,4,8,9}. 3.解答 ...
- 最长上升子序列(LIS经典变型) dp学习~5
题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=1069 Monkey and Banana Time Limit: 2000/1000 MS (Java ...
- 【洛谷4045】[JSOI2009] 密码(状压+AC自动机上DP)
点此看题面 大致题意: 给你\(n\)个字符串,问你有多少个长度为\(L\)的字符串,使得这些字符串都是它的子串.若个数不大于\(42\),按字典序输出所有方案. 状压 显然,由于\(n\)很小,我们 ...
- 对最长公共子序列(LCS)等一系列DP问题的研究
LIS问题: 设\(f[i]\)为以\(a[i]\)结尾的最长上升子序列长度,有: \[f[i]=f[j]+1(j<i&&a[j]<a[i])\] 可以用树状数组优化至\( ...
- 动态规划_基础_最长公共子序列_多种方法_递归/dp
D: 魔法少女资格面试 题目描述 众所周知,魔法少女是一个低危高薪职业.随着近年来报考魔法少女的孩子们越来越多,魔法少女行业已经出现饱和现象!为了缓和魔法少女界的就业压力,魔法少女考核员丁丁妹决定增加 ...
- 从最长公共子序列问题理解动态规划算法(DP)
一.动态规划(Dynamic Programming) 动态规划方法通常用于求解最优化问题.我们希望找到一个解使其取得最优值,而不是所有最优解,可能有多个解都达到最优值. 二.什么问题适合DP解法 如 ...
随机推荐
- sed进阶
下面这些命令未必经常会用到,但当需要时,知道这些肯定是件好事. 一.多行命令 sed命令通常是对一行数据进行处理,然后下一行重复处理. sed编辑器包含了三个可用来处理多行文本的特殊命令 N:将数据流 ...
- 【テンプレート】RMQ
1174 区间中最大的数 基准时间限制:1 秒 空间限制:131072 KB 收藏 关注 给出一个有N个数的序列,编号0 - N - 1.进行Q次查询,查询编号i至j的所有数中,最大的数是多少. ...
- BUUCTF |[0CTF 2016]piapiapia
步骤: nickname[]=wherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewherewhere ...
- NSProxy实现AOP方便为ios应用实现异常处理策略
前段时间关注过objc实现的AOP. 在GitHub找到了其中的两个库:AOP-in-Objective-C 和 AOP-for-Objective-C 第一个是基于NSProxy来实现的:第二个是基 ...
- vue实现动态显示与隐藏底部导航的方法分析
本文实例讲述了vue实现动态显示与隐藏底部导航的方法.分享给大家供大家参考,具体如下: 在日常项目中,总有几个页面是要用到底部导航的,总有那么些个页面,是不需要底部导航的,这里列举一下页面底部导航的显 ...
- selenium中的多窗口切换
在selenium中,我们会遇到一些问题,就是多窗口处理的问题,我们爬取的内容在一个新窗口上,这个时候,我们就需要先切换到这个新的窗口上,然后进行抓取内容. 如何切换呢? 首先,获取当前窗口句柄 1. ...
- 云计算openstack共享组件——Memcache 缓存系统
一.缓存系统 静态web页面: 1.工作流程: 在静态Web程序中,客户端使用Web浏览器(IE.FireFox等)经过网络(Network)连接到服务器上,使用HTTP协议发起一个请求(Reques ...
- Linux 初始化系统 systemd - journald 日志
journalctl 中文手册 archlinux - journal systemd-journald 用于检索 systemd 的日志,是 systemd 自带的日志系统. 1. systemd- ...
- [BZOJ2138]stone(Hall定理,线段树)
Description 话说Nan在海边等人,预计还要等上M分钟.为了打发时间,他玩起了石子.Nan搬来了N堆石子,编号为1到N,每堆 包含Ai颗石子.每1分钟,Nan会在编号在\([L_i,R_i] ...
- 为什么 Kafka 速度那么快?
来源:cnblogs.com/binyue/p/10308754.html Kafka的消息是保存或缓存在磁盘上的,一般认为在磁盘上读写数据是会降低性能的,因为寻址会比较消耗时间,但是实际上,Kafk ...