线性dp
经典问题
最长上升子序列
二分以后可以发现每个司机的起点和终点就确定了,那么按照起点排序排序后求终点的
考虑 求解 的方案数
那么可以发现 可以转移到 的条件是 并且
可以发现如果把所有 的位置拿出来,那么可以发现所有位置的 是单调递减的
直接双指针扫描即可
首先有性质 与 最多有一个交点
那么如果一个 与所有 都相交则不合法,否则一定合法
求出一个辅助值 表示强制选 的 方案数
然后找到两个 不同的 即可
由于 是严格单调的,中间可能不能变过去
那么对于 来说,如果可以相邻,那么 必须满足
那么对于 跑 即可
对于第二问,有结论是对于一个在 中的区间 ,之间的数一定存在一个分界点 ,使得 都变成 , 都变成
最长公共子序列
朴素的 是
当 是排列时可以将 按 排序然后跑
对于方案数,在朴素统计时需要注意如果 是需要减去 (类似于二维前缀和)
杂题
求长度为 本质不同子序列个数
设 表示 位置长度为 的子序列个数
那么
可以发现在这种条件下用容斥的减法会变得简洁
代码
#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;
}
似乎这种二叉树类型的题常常是用这种设树根的方式解决
设 表示大小为 的子树方案数
那么直接枚举左右大小,然后一直乘组合数就好了
最神奇的是这个组合数拆开后可以用递推出来,貌似组合数常常会有这种情况
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
· SQL Server 2025 AI相关能力初探
· 为什么 退出登录 或 修改密码 无法使 token 失效