线性dp

经典问题

最长上升子序列

C. 老司机的狂欢

二分以后可以发现每个司机的起点和终点就确定了,那么按照起点排序排序后求终点的 LIS


B. 降雷皇

考虑 O(n) 求解 LIS 的方案数
那么可以发现 j 可以转移到 i 的条件是 fj=fi1 并且 aj<ai
可以发现如果把所有 fi 的位置拿出来,那么可以发现所有位置的 a 是单调递减的
直接双指针扫描即可


D. Sanrd

首先有性质 LISLDS 最多有一个交点
那么如果一个 LIS 与所有 LDS 都相交则不合法,否则一定合法
求出一个辅助值 fi 表示强制选 iLDS 方案数
然后找到两个 f 不同的 LIS 即可


P2501 [HAOI2006]数字序列

由于 a 是严格单调的,中间可能不能变过去
那么对于 i,j 来说,如果可以相邻,那么 i,j 必须满足 aiajij
那么对于 bi=aiiLIS 即可
对于第二问,有结论是对于一个在 LIS 中的区间 [i,j],之间的数一定存在一个分界点 k,使得 [i+1,k] 都变成 bi[k+1,j1] 都变成 bj


最长公共子序列

朴素的 dpf[i][j]=max(f[i1][j],f[i][j1],f[i1][j1]+1[ai=bj]
a,b 是排列时可以将 ba 排序然后跑 LIS
对于方案数,在朴素统计时需要注意如果 ai!=bj 是需要减去 f[i1][j1](类似于二维前缀和)


杂题

C. 抛硬币

求长度为 l 本质不同子序列个数

f[i][j] 表示 i 位置长度为 j 的子序列个数
那么 f[i][j]=f[i1][j1]+f[i1][j]f[lasti1][j1]

可以发现在这种条件下用容斥的减法会变得简洁

代码
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005;
const int mod=998244353;
char c[maxn];
int len,n,last[130],f[maxn][maxn],ans,cnt,l[maxn];
bool vis[maxn];
signed main(){
	scanf("%s",c+1);
	cin>>len;
	n=strlen(c+1);
	for(int i=1;i<=n;i++){
		l[i]=last[c[i]];
		last[c[i]]=i;
		if(!vis[c[i]])cnt++,vis[c[i]]=1;
		f[i][1]=cnt;
		if(!l[i])l[i]++;
//		cout<<l[i]<<" ";
	}
	for(int i=1;i<=n;i++){
		for(int j=1;j<=len;j++){
			if(!f[i][j])f[i][j]=(((f[i][j]+f[i-1][j])%mod+f[i-1][j-1])%mod-f[l[i]-1][j-1]+mod)%mod;	
		}
	}
	cout<<(f[n][len]%mod+mod)%mod;
	return 0;
}

链式反应

似乎这种二叉树类型的题常常是用这种设树根的方式解决
fi 表示大小为 i 的子树方案数
那么直接枚举左右大小,然后一直乘组合数就好了
最神奇的是这个组合数拆开后可以用递推出来,貌似组合数常常会有这种情况

posted @   y_cx  阅读(46)  评论(0编辑  收藏  举报
(评论功能已被禁用)
相关博文:
阅读排行:
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效
点击右上角即可分享
微信分享提示