汕头市队赛 SRM 08 B

B-3 SRM 08

描述

给长度为 n 的数列 A 和长度为 m 的数列 B,问有多少长度为 m 的数列 C 满足

1 leq C_1<C_2<...<C_mleq n

(A_{c_1}+B_1) leq (A_{c_2}+B_2) leq ... leq (A_{c_m}+B_m)

输入格式

第一行俩整数 n 和 m

第二行 n 个整数 A_i,表示数列 A

第三行 m 个整数 B_i,表示数列 B

输出格式

一个整数,表示满足条件的数列 C 的个数模 10^9+7 后的值。

样例输入 1

5 3
1 5 2 4 7
7 9 6

样例输出 1

4

样例输入 2

4 2
7 7 7 7
3 4

样例输出 2

6

数据范围与约定

  • 1 leq A_i, B_i leq 10^9
  • 1 leq m leq n
  • 1 leq n leq 2000, 1leq m leq 1000

样例解释

第一个样例中,数列 C 可以为 (1, 3, 5), (1, 4, 5), (2, 4, 5), (3, 4, 5)

第二个样例中,数列 C 可以为 (1, 2), (1, 3), (1, 4), (2, 3), (2, 4), (3, 4)

这道题呢 很容易想到n^2m复杂度的dp f[i][j] 表示以j结尾长度为i(即匹配到第二个队列的第i个)的符合串

水了点分

#include<cstdio>
#include<cstring>
#include<algorithm>
const int M=2005,mod=1e9+7;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
    return ans*f;
}
int n,m,ans;
int a[M],b[M],f[M][1005];
int main()
{
    n=read(); m=read();
    for(int i=1;i<=n;i++) a[i]=read(),f[i][1]=1;
    for(int i=1;i<=m;i++) b[i]=read();
    for(int i=2;i<=m;i++){
        for(int j=i;j<=n;j++){
            for(int k=1;k<j;k++) 
                if(a[k]+b[i-1]<=a[j]+b[i]) f[j][i]=(f[j][i]+f[k][i-1])%mod;
        }
    }
    for(int i=m;i<=n;i++) ans=(ans+f[i][m])%mod;
    printf("%d
",ans);
    return 0;
}
View Code

 当然 这时间复杂度n一大绝对超时 这个时候就要想怎么降低时间复杂度了 

观察后发现第三个循环枚举j前面所有点的时候 我们维护的信息明显可以用树状数组维护

我们需要的点的信息要满足 i<j && k[p+1]+b[i-1]<=k[j]+b[i]

那么 我们可以先给k【i】排一波序 然后进行一波离散化 方便后面树状数组的操作

树状数组下标表示相对大小关系 

然后我们可以类似递推地求出 如果只考虑大小关系 有多少个数满足上式

考虑前后顺序 那么我们只要按原顺序逐渐插入树状数组中就可以保证了

然后就是单点修改上传信息以及区间求和了

#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int M=2007,mod=1e9+7;
int read(){
    int ans=0,f=1,c=getchar();
    while(c<'0'||c>'9'){if(c=='-') f=-1; c=getchar();}
    while(c>='0'&&c<='9'){ans=ans*10+(c-'0'); c=getchar();}
    return ans*f;
}
int n,m,cnt,f[M],last[M],ans;
int s[M],k[M],b[M],d[M],pos[M];
struct node{int w,pos;}q[M];
bool cmp(node a,node b){return a.w<b.w;}
int lowbit(int x){return x&-x;}
void add(int x,int v){
    while(x<=n){
        (s[x]+=v)%=mod;
        x+=lowbit(x);
    }
}
int query(int x){
    int ans=0;
    while(x) (ans+=s[x])%=mod,x-=lowbit(x);
    return ans;
}
int main()
{
    n=read(); m=read();
    for(int i=1;i<=n;i++) k[i]=read(),f[i]=1,q[i]=(node){k[i],i};
    sort(q+1,q+1+n,cmp);
    d[++cnt]=q[1].w; pos[q[1].pos]=1;
    for(int i=2;i<=n;i++){
        if(q[i].w!=q[i-1].w) d[++cnt]=q[i].w;
        pos[q[i].pos]=cnt; 
    }
    for(int i=1;i<=m;i++) b[i]=read();
    for(int i=2;i<=m;i++){
        for(int j=1;j<=cnt;j++){
            int p=last[j-1];
            while(p<cnt&&d[p+1]+b[i-1]<=d[j]+b[i]) p++;
            last[j]=p;
        }
        for(int j=1;j<=cnt;j++) s[j]=0;
        add(pos[i-1],f[i-1]);
        for(int j=i;j<=n;j++){
            int sum=f[j];
            f[j]=query(last[pos[j]]);
            add(pos[j],sum);
        }
    }
    for(int i=m;i<=n;i++) (ans+=f[i])%=mod;
    printf("%d
",ans);
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/lyzuikeai/p/7246866.html