一些有意思的科技(其二)
给两个字符串 \(A,B\) ,其长度分别为 \(n,m\) (同阶), \(q\) 次询问 \(l_1,r_1,l_2,r_2\) 求 \(A[l_1,r_1]\) 与 \(B[l_2,r_2]\) 的 \(\text{LCS}\)
有一个算法 \(\text{ALCS}\) 可以做到 \(O(n^2 \log n+qn)\) ,原论文在这里
考虑把最经典的 \(\text{DP}\) 看作走网格,差不多长这样,横竖边权都为 \(0\) ,斜边边权为 \(1\)
我们令 \((x,y)\) 表示从左往右的 \(x\) 列,从上往下第 \(y\) 行(从 \(0\) 开始),一共有 \(n+1\) 列, \(m+1\) 行
考虑询问怎么做,考虑二维的分治,每次劈开大的那一维,假设是将横着的分成两半(就是这个图从中竖劈),这个时候我们需要处理的是跨过这条线,假设其为第 \(x\) 列的询问,我们枚举从 \((l_1,l_2)\) 走到 \((r_1,r_2)\) 时第一次与这条线相交的位置 \(i\) ,我们想分别求出 \((x,i)\) 走到 \((l_1,l_2)\) 和 \((r_1,r_2)\) 的最长路
这样我们的问题就转化为了快速的求出一些 \(C^{l}(x,r)\) 表示在一张网格图上从 \((0,l)\) 走到 \((x,r)\) 的最长路,注意到有性质
若对于 \(1 \le l<r \le m\) ,有 \(C^{l-1}(x,r-1)+1=C^{l-1}(x,r)\) ,则有 \(C^{l}(x,r-1)+1=C^{l}(x,r)\)
若对于 \(1 \le l<r \le m,x>0\) ,有 \(C^{l-1}(x-1,r)=C^{l-1}(x,r)\) ,则有 \(C^{l}(x-1,r)=C^{l}(x,r)\)
证明注意到最靠左的最优的路径后缀不变即可
所以若我们能求出 \(ih_{x,y}\) 表示所有 \(C^{i}(x,y)\) 满足性质 \(1\) 的最小的 \(i\) ,\(iv_{x,y}\) 表示满足性质 \(2\) 的最小的 \(i\) ,转移讨论一下 \(a_x\) 和 \(b_y\) 是否相等即可,具体可以看代码
在求出 \(ih_{x,y}\) 以后,对于固定的 \((x,y)\) 可以通过区间加求出所有的 \(C^{i}(x,y)\) ,显然可以用差分做到 \(O(n)\) 求,这样就搞定了,可以看代码理解理解
点击查看代码
#include<cstdio>
#include<cctype>
#include<vector>
#include<algorithm>
#define maxn 2222
#define maxm 101101
template<class T>
inline T read(){
T r=0,f=0;
char c;
while(!isdigit(c=getchar()))f|=(c=='-');
while(isdigit(c))r=(r*10)+(c^48),c=getchar();
return f?-r:r;
}
template<class T>
inline T min(T a,T b){
return a<b?a:b;
}
template<class T>
inline T max(T a,T b){
return a>b?a:b;
}
template<class T>
inline void chkmax(T &a,T b){
a=a>b?a:b;
}
struct Q{
int l1,r1,l2,r2;
}qry[maxm];
int n,m,q,ans[maxm];
#define vec std::vector<int>
struct ALCS{
int iv[maxn][maxn],ih[maxn][maxn];
inline void init(vec &a,vec &b){
int n=a.size(),m=b.size();
for(int i=0;i<=n;i++)iv[i][0]=0;
for(int j=0;j<=m;j++)ih[0][j]=j;
for(int i=1;i<=n;i++)
for(int j=1;j<=m;j++){
if(a[i-1]^b[j-1]){
ih[i][j]=max(iv[i][j-1],ih[i-1][j]);
iv[i][j]=iv[i][j-1]^ih[i-1][j]^ih[i][j];
}
else ih[i][j]=iv[i][j-1],iv[i][j]=ih[i-1][j];
}
}
inline void calcrow(int m,int i,int *sum){
for(int j=0;j<=m;j++)sum[j]=0;
for(int j=1;j<=m;j++)
if(ih[i][j]<j)sum[ih[i][j]]++,sum[j]--;
for(int j=1;j<m;j++)sum[j]+=sum[j-1];
sum[m]=0,std::reverse(sum,sum+m+1);
}
}alcs[2];
int suml[maxn],sumr[maxn];
inline void calc(int mid,int m,vec &ask){
for(auto p:ask){
int l1=qry[p].l1,r1=qry[p].r1;
int l2=qry[p].l2,r2=qry[p].r2;
int len=r2-l2+1,len1=m-l2+1,len2=r2;
alcs[0].calcrow(len1,mid-l1+1,suml);
alcs[1].calcrow(len2,r1-mid,sumr);
for(int k=max(len-len2,0),lim=min(len,len1);k<=lim;k++)
chkmax(ans[p],suml[k]+sumr[len-k]);
}
}
inline void solve(vec &a,vec b,vec &ask){
if(ask.empty())return;
if(a.size()<b.size()){
std::swap(a,b);
for(auto p:ask){
std::swap(qry[p].l1,qry[p].l2);
std::swap(qry[p].r1,qry[p].r2);
}
}
int mid=a.size()>>1;
vec al=vec(a.begin(),a.begin()+mid);
vec ar=vec(a.begin()+mid,a.end());
std::reverse(al.begin(),al.end());
std::reverse(b.begin(),b.end());
alcs[0].init(al,b);
std::reverse(al.begin(),al.end());
std::reverse(b.begin(),b.end());
alcs[1].init(ar,b);
vec askl,askr,now;
for(auto p:ask){
if(qry[p].r1<mid)askl.push_back(p);
else if(qry[p].l1>mid){
qry[p].l1-=mid,qry[p].r1-=mid;
askr.push_back(p);
}
else now.push_back(p);
}
calc(mid,b.size(),now);
solve(al,b,askl),solve(ar,b,askr);
}
int cnta[maxn][maxn],cntb[maxn][maxn];
int main(){
n=read<int>();
m=read<int>();
q=read<int>();
vec a(n),b(m),ask;
for(int i=0,V=0;i<n;i++){
V=max(V,a[i]=read<int>());
for(int j=0;j<=V;j++)
cnta[i+1][j]=cnta[i][j]+(j==a[i]);
}
for(int i=0,V=0;i<m;i++){
V=max(V,b[i]=read<int>());
for(int j=0;j<=V;j++)
cntb[i+1][j]=cntb[i][j]+(j==b[i]);
}
for(int i=1;i<=q;i++){
qry[i].l1=read<int>();
qry[i].r1=read<int>();
qry[i].l2=read<int>();
qry[i].r2=read<int>();
if(qry[i].l1==qry[i].r1){
int x=a[qry[i].l1-1];
ans[i]=cntb[qry[i].r2][x]>cntb[qry[i].l2-1][x];
}
else if(qry[i].l2==qry[i].r2){
int x=b[qry[i].l2-1];
ans[i]=cnta[qry[i].r1][x]>cnta[qry[i].l1-1][x];
}
else ask.push_back(i);
}
solve(a,b,ask);
for(int i=1;i<=q;i++)
printf("%d\n",ans[i]);
return 0;
}