LOJ #6564 - 最长公共子序列(bitset 求 LCS)
怎么全天下就我没见过?被薄纱了/ll
还是考虑从朴素的 DP 入手优化。不难发现对于固定的 ,相邻的 的差要么是 要么是 ,也就是说从压位的考虑角度可能很有前途。因此我们转而维护 的差分数组 。考虑新添加一个元素 时候会发生什么变化:对于原来差分数组中每一段极长的连续段 满足 ,如果 中存在至少一个元素 ,那么我们拎出最靠左的那个,假设为 ,那么变化是 变为 , 变为 ,否则这段的差分数组保持不变。
但是不好用 bitset 直接表示。考虑将其转化为可以用 bitset 直接维护的操作:
- 令 表示原来的差分数组, 表示 向右平移一位的数组(即 )
- 令 为将所有匹配位置变为 后的数组,即对于所有 ,,其余 。
- 令 ,其中减法为二进制整数的减法,即将一个 01 数组看作一个大数,其中第 位上的值为该数 位上的值。
- 那么我们再令 每一位上的值与上 ,就是新的差分数组。
手玩一下就会发现上述过程与 DP 部分所述过程等价。
手写 bitset 即可。时间复杂度 。
const int MAXN=70000; const int B=MAXN/64+1; int n,m,a[MAXN+5],b[MAXN+5]; u64 pos[MAXN+5][B+5],f[B+5],g[B+5],h[B+5]; int main(){ scanf("%d%d",&n,&m); for(int i=1;i<=n;i++)scanf("%d",&a[i]); for(int i=1;i<=m;i++)scanf("%d",&b[i]); for(int i=1;i<=n;i++)pos[a[i]][i>>6]|=(1ull<<(i&63)); for(int i=1;i<=m;i++){ memset(g,0,sizeof(g));memset(h,0,sizeof(h)); for(int j=0;j<=B;j++)g[j]|=f[j]<<1,g[j+1]|=(f[j]>>63&1); g[0]|=2; for(int j=0;j<=B;j++)f[j]|=pos[b[i]][j]; u64 c=0; for(int j=0;j<=B;j++){ h[j]=f[j]-g[j]-c; c=(c&&(f[j]==0))||((u64)(f[j]-c)<g[j]); } for(int j=0;j<=B;j++)f[j]&=(f[j]^h[j]); } int sum=0; for(int i=0;i<=B;i++)sum+=__builtin_popcountll(f[i]); printf("%d\n",sum); return 0; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
2021-04-20 Atcoder Regular Contest 117 D - Miracle Tree(分析性质+构造)