洛谷题面传送门

首先看到 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+打表)的更多相关文章

  1. 【bzoj5161】最长上升子序列 状压dp+打表

    题目描述 现在有一个长度为n的随机排列,求它的最长上升子序列长度的期望. 为了避免精度误差,你只需要输出答案模998244353的余数. 输入 输入只包含一个正整数n.N<=28 输出 输出只包 ...

  2. 洛谷P3959 宝藏(NOIP2017)(状压DP,子集DP)

    洛谷题目传送门 Dalao的题解多数是什么模拟退火.DFS剪枝.\(O(3^nn^2)\)的状压DP之类.蒟蒻尝试着把状压改进了一下使复杂度降到\(O(3^nn)\). 考虑到每条边的贡献跟它所在的层 ...

  3. BZOJ.3591.最长上升子序列(状压DP)

    BZOJ 题意:给出\(1\sim n\)的一个排列的一个最长上升子序列,求原排列可能的种类数. \(n\leq 15\). \(n\)很小,参照HDU 4352这道题,我们直接把求\(LIS\)时的 ...

  4. 洛谷P1896 [SCOI2005]互不侵犯King【状压DP】

    题目描述 在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 输入格式: 只有一行,包含两个数N,K ...

  5. 【洛谷 P1896】[SCOI2005]互不侵犯(状压dp)

    题目链接 题意:在N×N的棋盘里面放K个国王,使他们互不攻击,共有多少种摆放方案.国王能攻击到它上下左右,以及左上左下右上右下八个方向上附近的各一个格子,共8个格子. 这是道状压\(DP\)好题啊.. ...

  6. BZOJ 2734 洛谷 3226 [HNOI2012]集合选数【状压DP】【思维题】

    [题解] 思维题,看了别人的博客才会写. 写出这样的矩阵: 1,3,9,... 2,6,18,... 4,12.36,... 8,24,72,... 我们要做的就是从矩阵中选出一些数字,但是不能选相邻 ...

  7. 【洛谷5492】[PKUWC2018] 随机算法(状压DP)

    点此看题面 大致题意: 用随机算法求一张图的最大独立集:每次随机一个排列,从前到后枚举排列中的点,如果当前点加入点集中依然是独立集,就将当前点加入点集中,最终得到的点集就是最大独立集.求这个随机算法的 ...

  8. BZOJ 5161: 最长上升子序列 状压dp+查分

    好神啊 ~ 打表程序: #include <cstdio> #include <cstring> #include <algorithm> #define N 14 ...

  9. 洛谷P2396 yyy loves Maths VII【状压dp】

    题目:https://www.luogu.org/problemnew/show/P2396 题意:有n个数,每次选择一个表示走$a[i]$步,每个数只能选一次. 最多有两个厄运数字,如果走到了厄运数 ...

随机推荐

  1. for...in和Object.keys()区别

    区别: for in 用来枚举对象的属性,某些情况下,可能按照随机顺序遍历数组元素 object.keys() 可以返回对象属性为元素的数组,数组中属性名顺序和for in比那里返回顺序一样 ---f ...

  2. Flink sql 之 join 与 StreamPhysicalJoinRule (源码解析)

    源码分析基于flink1.14 Join是flink中最常用的操作之一,但是如果滥用的话会有很多的性能问题,了解一下Flink源码的实现原理是非常有必要的 本文的join主要是指flink sql的R ...

  3. MySQL:提高笔记-5

    MySQL:提高笔记-5 学完基础的语法后,进一步对 MySQL 进行学习,前几篇为: MySQL:提高笔记-1 MySQL:提高笔记-2 MySQL:提高笔记-3 MySQL:提高笔记-4 MySQ ...

  4. [no code][scrum meeting] Beta 7

    $( "#cnblogs_post_body" ).catalog() 例会时间:5月21日15:30,主持者:彭毛小民 下次例会时间:5月22日15:30,主持者:赵涛 昨日为5 ...

  5. echart3 力引导布局实现节点的提示和折叠

    最近在项目中需要开发一个图表来显示人员的各种属性,类似于一种树形的结构进行显示数据.如果多个人员有同一个属性,那么需要将相同的属性进行连线,即关联起来.即形成一个关系图,由于我自身对echarts稍微 ...

  6. Go语言核心36讲(Go语言进阶技术八)--学习笔记

    14 | 接口类型的合理运用 前导内容:正确使用接口的基础知识 在 Go 语言的语境中,当我们在谈论"接口"的时候,一定指的是接口类型.因为接口类型与其他数据类型不同,它是没法被实 ...

  7. 21.6.17 test

    \(NOI\) 模拟赛. \(T1\) 正解树形DP,由于不是很熟悉概率和期望所以打了个20pts暴力,说不定见多了概率能打出60pts半正解?最后的虚树更不会. \(T2\) 又是概率,还有坐标数量 ...

  8. 按之字形顺序打印二叉树 牛客网 剑指Offer

    按之字形顺序打印二叉树 牛客网 剑指Offer 题目描述 请实现一个函数按照之字形打印二叉树,即第一行按照从左到右的顺序打印,第二层按照从右至左的顺序打印,第三行按照从左到右的顺序打印,其他行以此类推 ...

  9. 访问单个结点的删除 牛客网 程序员面试金典 C++ Python

    访问单个结点的删除 牛客网 程序员面试金典 C++ Python 题目描述 实现一个算法,删除单向链表中间的某个结点,假定你只能访问该结点. 给定待删除的节点,请执行删除操作,若该节点为尾节点,返回f ...

  10. 几个简单的文本处理工具:diff,patch,grep,cut,sort,tr

    1:文本处理工具:   1:diff and patch : diff是比较文件或者目录的不同,主要有3个用法: diff file1 file2 :比较file1和file2的不同:diff -u ...