LOJ #6564 - 最长公共子序列(bitset 求 LCS)

怎么全天下就我没见过?被薄纱了/ll

还是考虑从朴素的 DP 入手优化。不难发现对于固定的 i,相邻的 dpi,j 的差要么是 0 要么是 1,也就是说从压位的考虑角度可能很有前途。因此我们转而维护 dpi,j 的差分数组 vi,j=dpi,jdpi,j1。考虑新添加一个元素 ai+1 时候会发生什么变化:对于原来差分数组中每一段极长的连续段 [l,r] 满足 vi,l=vi,l+1==vi,r1=0,vi,r=1,如果 bl,bl+1,,br1 中存在至少一个元素 =ai+1,那么我们拎出最靠左的那个,假设为 bj,那么变化是 vi+1,j 变为 1vi+1,r 变为 0,否则这段的差分数组保持不变。

但是不好用 bitset 直接表示。考虑将其转化为可以用 bitset 直接维护的操作:

  • f 表示原来的差分数组,g 表示 f 向右平移一位的数组(即 gi+1=fi,g1=1
  • f 为将所有匹配位置变为 1 后的数组,即对于所有 bj=ai+1fj=1,其余 fj=fj
  • g=fg,其中减法为二进制整数的减法,即将一个 01 数组看作一个大数,其中第 i 位上的值为该数 2i 位上的值。
  • 那么我们再令 f 每一位上的值与上 (figi),就是新的差分数组。

手玩一下就会发现上述过程与 DP 部分所述过程等价。

手写 bitset 即可。时间复杂度 nmω

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;
}
posted @   tzc_wk  阅读(511)  评论(17编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!
· AI技术革命,工作效率10个最佳AI工具
历史上的今天:
2021-04-20 Atcoder Regular Contest 117 D - Miracle Tree(分析性质+构造)
点击右上角即可分享
微信分享提示