洛谷 P4484 - [BJWC2018]最长上升子序列(状压 dp+打表)
首先看到 LIS 我们可以想到它的 \(\infty\) 种求法(bushi),但是对于此题而言,既然题目出这样一个数据范围,硬要暴搜过去也不太现实,因此我们需想到用某种奇奇怪怪的方式进行状态压缩 DP,这样一来就可以排除掉不少常用的求 DP 的方法:譬如最常用的从左往右顺着钦定元素并设 \(f_i\) 表示以 \(i\) 结尾的 LIS 的长度的方法,因此考虑换个角度,从小到大添加元素。还是设 \(f_i\) 表示以 \(i\) 结尾的 LIS 的长度,那么考虑在一轮中,我们在 \(i\) 和 \(i+1\) 这两个位置中间插入一个比当前所有数都大的数会对 \(f\) 产生怎样的影响,有:
- \(f_{i+1}=1+\max\limits_{j=1}^if_j\)
- 对于 \(j>i\),有新的 \(f_{j+1}\) 等于 \(f_j\)。
注意到这里涉及前缀 \(\max\),因此设 \(mf_i=\max\limits_{j=1}^if_j\),又注意到相邻两个 \(mf_i\) 的差最多为 \(1\),因此考虑 \(mf_i\) 的差分序列 \(d_i=mf_i-mf_{i-1}\),根据之前的推论显然它是一个 \(01\) 序列,这样就天然地形成了状压 \(dp\) 的模型。由于我们的 \(dp\) 都建立在差分序列的基础上,因此我们再来探究下加入一个比当前所有数都大的数会对差分序列产生怎样的影响:
- \(d_{i+1}=1\)
- 对于所有 \(j\ge i+1\),有新的 \(d_{j+1}\) 等于原来的 \(d_j\)。
- 进行以上两个操作之后,我们找到 \(i+1\) 后面第一个 \(d_j=1\) 的 \(j\)——如果不存在这样的 \(j\) 则跳过这一步,并令 \(d_j=0\)。
这样我们就可以设 \(dp_{i,j}\) 表示当前插入了 \(1\sim i\),\(d\) 的状态为 \(j\) 的方案数,转移就枚举在哪里插入 \(i+1\),然后二进制模拟 \(d\) 的变化即可。注意到 \(j\) 只用枚举到 \(2^i\),因此 \((i,j)\) 的总枚举量是 \(2^n\) 的,再加上转移的 \(n\),总复杂度 \(2^n·n\),注意滚动数组优化空间,否则会获得 MLE 0 的好成绩。
void add(int &x,int v){((x+=v)>=MOD)&&(x-=MOD);}
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,dp[2][134217733];
int main(){
scanf("%d",&n);
dp[1][1>>1]=1;int nxt=0,cur=1;
for(int i=1;i<n;i++){
for(int j=0;j<(1<<i+1);j++) dp[nxt][j>>1]=0;
for(int j=1;j<(1<<i);j+=2) if(dp[cur][j>>1]){
// printf("%d %d %d\n",i,j,dp[cur][j]);
for(int k=0;k<=i;k++){
int nmsk=0;
if(k) nmsk=j&((1<<k)-1);
nmsk|=(1<<k);
int rst=((1<<i)-1)^((!k)?0:((1<<k)-1));
nmsk|=(j&rst)<<1;
if((j&rst)){int S=j&rst;nmsk^=(S&(-S))<<1;}
add(dp[nxt][nmsk>>1],dp[cur][j>>1]);
}
} swap(cur,nxt);
} int res=0,fac=1;
for(int i=1;i<(1<<n);i+=2) res=(res+1ll*__builtin_popcount(i)*dp[cur][i>>1])%MOD;
for(int i=1;i<=n;i++) fac=1ll*fac*i%MOD;
res=1ll*res*qpow(fac,MOD-2)%MOD;
printf("%d\n",res);
return 0;
}
直接按照上面的做法写大约会 TLE 3~6 个点,取决于你的实现,不过注意到这题长得一脸打表的样子,于是你对 \(n\) 比较大的情况在本地跑一下上面的代码,算出答案特判下即可通过此题。
并没有看懂楼下 EI 神仙的题解
void add(int &x,int v){((x+=v)>=MOD)&&(x-=MOD);}
int qpow(int x,int e){
int ret=1;
for(;e;e>>=1,x=1ll*x*x%MOD) if(e&1) ret=1ll*ret*x%MOD;
return ret;
}
int n,dp[2][134217733];
int main(){
scanf("%d",&n);
if(n==22) return puts("749077581"),0;
if(n==23) return puts("301075008"),0;
if(n==24) return puts("314644758"),0;
if(n==25) return puts("102117126"),0;
if(n==26) return puts("819818153"),0;
if(n==27) return puts("273498600"),0;
if(n==28) return puts("267588741"),0;
dp[1][1>>1]=1;int nxt=0,cur=1;
for(int i=1;i<n;i++){
for(int j=0;j<(1<<i+1);j++) dp[nxt][j>>1]=0;
for(int j=1;j<(1<<i);j+=2) if(dp[cur][j>>1]){
// printf("%d %d %d\n",i,j,dp[cur][j]);
for(int k=0;k<=i;k++){
int nmsk=0;
if(k) nmsk=j&((1<<k)-1);
nmsk|=(1<<k);
int rst=((1<<i)-1)^((!k)?0:((1<<k)-1));
nmsk|=(j&rst)<<1;
if((j&rst)){int S=j&rst;nmsk^=(S&(-S))<<1;}
add(dp[nxt][nmsk>>1],dp[cur][j>>1]);
}
} swap(cur,nxt);
} int res=0,fac=1;
for(int i=1;i<(1<<n);i+=2) res=(res+1ll*__builtin_popcount(i)*dp[cur][i>>1])%MOD;
for(int i=1;i<=n;i++) fac=1ll*fac*i%MOD;
res=1ll*res*qpow(fac,MOD-2)%MOD;
printf("%d\n",res);
return 0;
}
洛谷 P4484 - [BJWC2018]最长上升子序列(状压 dp+打表)的更多相关文章
- 【bzoj5161】最长上升子序列 状压dp+打表
题目描述 现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望. 为了避免精度误差,你只需要输出答案模998244353的余数. 输入 输入只包含一个正整数n.N<=28 输出 输出只包 ...
- 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)
洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...
- BZOJ.3591.最长上升子序列(状压DP)
BZOJ 题意:给出\(1\sim n\)的一个排列的一个最长上升子序列,求原排列可能的种类数. \(n\leq 15\). \(n\)很小,参照HDU 4352这道题,我们直接把求\(LIS\)时的 ...
- 洛谷P1896 [SCOI2005]互不侵犯King【状压DP】
题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入格式: 只有一行,包含两个数N,K ...
- 【洛谷 P1896】[SCOI2005]互不侵犯(状压dp)
题目链接 题意:在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 这是道状压\(DP\)好题啊.. ...
- BZOJ 2734 洛谷 3226 [HNOI2012]集合选数【状压DP】【思维题】
[题解] 思维题,看了别人的博客才会写. 写出这样的矩阵: 1,3,9,... 2,6,18,... 4,12.36,... 8,24,72,... 我们要做的就是从矩阵中选出一些数字,但是不能选相邻 ...
- 【洛谷5492】[PKUWC2018] 随机算法(状压DP)
点此看题面 大致题意: 用随机算法求一张图的最大独立集:每次随机一个排列,从前到后枚举排列中的点,如果当前点加入点集中依然是独立集,就将当前点加入点集中,最终得到的点集就是最大独立集.求这个随机算法的 ...
- BZOJ 5161: 最长上升子序列 状压dp+查分
好神啊 ~ 打表程序: #include <cstdio> #include <cstring> #include <algorithm> #define N 14 ...
- 洛谷P2396 yyy loves Maths VII【状压dp】
题目:https://www.luogu.org/problemnew/show/P2396 题意:有n个数,每次选择一个表示走$a[i]$步,每个数只能选一次. 最多有两个厄运数字,如果走到了厄运数 ...
随机推荐
- MongoDB中如何优雅地删除大量数据
删除大量数据,无论是在哪种数据库中,都是一个普遍性的需求.除了正常的业务需求,我们需要通过这种方式来为数据库"瘦身". 为什么要"瘦身"呢? 表的数据量到达一定 ...
- 关于takin-data,你想知道的都在这里(一)启动命令篇
通过docker部署体验takin的小伙伴都应该知道,在安装部署手册中有提到:在启动surge-deploy任务前,需要将启动命令中的ip参数替换为docker容器所在宿主机的ip,很多小伙伴都在这里 ...
- Java继承中父类和子类构造函数的问题
父类有无参构造函数时(显示或隐式),子类的有参和无参构造函数都是默认调用父类的无参构造函数:当父类只有有参构造函数时,子类可以有有参和无参构造函数,子类有参构造函数必须显式调用父类的有参构造函数,子类 ...
- 零基础如何更好的学习Linux
本节旨在介绍对于初学者如何学习 Linux 的建议.如果你已经确定对 Linux 产生了兴趣,那么接下来我们介绍一下学习 Linux 的方法. 如何去学习 学习大多类似庖丁解牛,对事物的认识一般都是由 ...
- 最短路径算法:弗洛伊德(Floyd-Warshall)算法
一.算法介绍 Floyd-Warshall算法(英语:Floyd-Warshall algorithm),中文亦称弗洛伊德算法,是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权(但不可存 ...
- uvm中类继承和phase
1,uvm中类继承关系如下所示 2,uvm中phase如下所示
- Ubuntu 16.04 curl 安装 使用
curl是利用URL语法在命令行方式显工作的开元文件传输工具. 安装 $ sudo apt install -y curl 使用 $ curl http://www.baidu.com 这是最简单的使 ...
- 最近公共祖先(lca)与树上叉分
lca的定义不在过多解释, 代码如下: inline void bfs() { queue<int>q; deep[s]=1;q.push(s); while(!q.empty()) { ...
- 并发编程从零开始(十四)-Executors工具类
并发编程从零开始(十四)-Executors工具类 12 Executors工具类 concurrent包提供了Executors工具类,利用它可以创建各种不同类型的线程池 12.1 四种对比 单线程 ...
- C++实现一个SOAP客户端
目录 简介 实现客户端 准备xml文件 引入库文件 构建请求数据的xml 执行Http协议的POST方法 解析响应数据的xml 测试客户端 附件 简介 在C++中,一般使用gSOAP来实现客户端.服务 ...