题意:给出两个序列An,Bn,有多少对等长的子序列Ai-j和Bi-j之间的距离小于等于v,两个子序列的距离定义为 ∑ (|ai-bi|^p) (p<=3)
暴力的做法是n^3枚举两个区间起点和最大长度,考虑优化。
以Ai和Bj为起点时,因为距离公式的每一项都非负,显然最大长度至少是以Ai-1和Bj-1为起点是的最大长度减一。因此每次暴力的时候记录下最大长度和这一段的距离,之后就可以再次使用。因为区间右端点是单调递增的,也就是长度最多扩大n次。类似于双指针,可以证明复杂度是O(n^2)的。
之前选拔赛也做过一道类似的双指针题,这类题目就是利用单调性和之前的计算结果减少枚举次数,降低复杂度。本质上还是暴力。
#include<bits/stdc++.h> using namespace std; #define LL long long #define rep(i,n) for(int i=1;i<=n;i++) const int N=1005; LL a[N],b[N]; int l[N][N]; LL sum[N][N]; LL n,v,p; inline LL f(int i,int j) { LL res=1,x=abs(a[i]-b[j]); rep(i,p)res*=x; return res; } int main() { int t; scanf("%d",&t); while(t--) { scanf("%lld%lld%lld",&n,&v,&p); rep(i,n)scanf("%lld",&a[i]); rep(i,n) scanf("%lld",&b[i]); LL ans=0; rep(i,n) { rep(j,n) { LL now=sum[i-1][j-1]-f(i-1,j-1); int k; for(k=l[i-1][j-1]-1; i+k<=n&&j+k<=n; k++) { if(now+f(i+k,j+k)<=v) now+=f(i+k,j+k); else break; } sum[i][j]=now; l[i][j]=k; ans+=k; } } printf("%lld\n",ans); } return 0; }