[科技]Loj#6564-最长公共子序列【bitset】

正题

题目链接:https://loj.ac/p/6564


题目大意

给两个序列\(a,b\)求它们的最长公共子序列。

\(1\leq n,m,a_i,b_i\leq 7\times 10^4\)


解题思路

无意间看到的一个\(bitset\)科技。

首先设\(f_{i,j}\)表示\(a\)串匹配到第\(i\)\(b\)串匹配到第\(j\)个时的最长长度,做过\(dp\)\(dp\)的应该知道\(f_{i,j}\)的性质。

\[0\leq f_{i,j}-f_{i,j-1}\leq 1 \]

基本的思路就是设\(01\)矩阵\(M\)满足\(f_{i,j}=\sum_{k=1}^jM_{i,k}\)然后用\(bitset\)优化转移

然后考虑一下怎么转移,我们先预处理出\(p\)数组其中\(p_i\)表示数字\(i\)出现的位置集合

我们的转移要把\(M\)中的\(1\)尽量的往前移动并且看能否加上一个新的\(1\)

假设现在的字符是\(c\),那么我们将使用\(p_c\)进行转移。

我们把\(M\)中每个\(1\)作为结尾分成若干段(最后的\(0\)也是一段,顺序是从低位到高位)。

那么对于一段中如果这一段\(p_c\)\(1\)那么我们就取最前面的那个\(1\),这样因为前面假设有\(j\)\(1\)那么这次就匹配\(p_c\)最前面的那个作为\(j+1\)

但是我们显然不可能一段一段做,我们可以考虑怎么把这个操作转成位运算🤔。

考虑一下我们平时是怎么取一个\(01\)串的第一位的,我们有\(lowbit(x)=((x-1)\ xor\ x)\ and\ x\)对吧。

发现这里每段分开取实际上难实现的地方只有\(x-1\),考虑怎么实现这个问题。

因为\(1\)是段的末尾,所以每一段的开头前面都是\(1\),所以如果我们让\(M\)左移一位那么就变成开头是\(1\)了(需要注意补上第一段的\(1\),所以应该是\((M<<1)+1\)

最后来说是

\[M=(X-(M<<1)+1)\ xor\ X\ and\ X \]

这样我们就完成了\(M\)的转移,因为需要位运算,所以需要手写\(bitset\)

时间复杂度\(O(\frac{nm}{\omega})\)

我是看这篇博客学的,看不懂的可以去看下:https://www.cnblogs.com/-Wallace-/p/bit-lcs.html


code

#include<cstdio>
#include<cstring>
#include<algorithm>
#define ull unsigned long long
using namespace std;
const int N=7e4+10;
int n,m,L;
struct bitset{
	ull t[N/64+5];
	bitset(){memset(t,0,sizeof(t));return;}
	void set(int p){
		t[p>>6]|=(1ull<<(p&63));
		return;
	}
	void shift(){
		ull last=0;
		for(int i=0;i<L;i++){
			ull cur=t[i]>>63;
			(t[i]<<=1)|=last;
			last=cur;
		}
		return;
	}
	int count(){
		int ans=0;
		for(int i=0;i<L;i++)
		{ull x=t[i];while(x)x-=(x&-x),ans++;}
		return ans;
	}
	bitset& operator=(const bitset &b)
	{memcpy(t,b.t,sizeof(t));return *this;}
	bitset& operator|=(const bitset &b){
		for(int i=0;i<L;i++)t[i]|=b.t[i];
		return *this;
	}
	bitset& operator&=(const bitset &b){
		for(int i=0;i<L;i++)t[i]&=b.t[i];
		return *this;
	}
	bitset& operator^=(const bitset &b){
		for(int i=0;i<L;i++)t[i]^=b.t[i];
		return *this;
	}
}p[N],f,g;
bitset operator-(const bitset &a,const bitset &b){
	bitset tmp;ull last=0;
	for(int i=0;i<L;i++){
		ull cur=(a.t[i]<b.t[i]+last);
		tmp.t[i]=a.t[i]-b.t[i]-last;
		last=cur;
	}
	return tmp;
}
int main()
{
	scanf("%d%d",&n,&m);L=(n>>6)+1;
	for(int i=1,c;i<=n;i++)
		scanf("%d",&c),p[c].set(i);
	for(int i=1,c;i<=m;i++){
		scanf("%d",&c);
		(g=f)|=p[c];
		f.shift();f.set(0);
		f=g-f;f^=g;f&=g;
	}
	printf("%d",f.count());
	return 0;
}
posted @ 2021-07-02 14:24  QuantAsk  阅读(225)  评论(0编辑  收藏  举报