[ZOJ]ZOJ4003(双指针)

题意:给出两个序列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;
}

 

posted on 2019-03-25 00:54  枫棠  阅读(180)  评论(0编辑  收藏  举报

导航