正题
题目大意
给出一个长度为 n n n的序列 a a a和数字 l l l,定义两个长度为 l l l的区间 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]和 [ l 2 , r 2 ] [l_2,r_2] [l2,r2]的距离为有多少个不相同的数字。
然后有 q q q个询问 k i k_i ki,要求输出有多少对距离为 k i k_i ki的区间。
解题思路
我们发现若我们求出了 [ l 1 , r 1 ] [l_1,r_1] [l1,r1]和 [ l 2 , r 2 ] [l_2,r_2] [l2,r2]的距离,就可以 O ( 1 ) O(1) O(1)求出 [ l 1 + 1 , r 1 + 1 ] [l_1+1,r_1+1] [l1+1,r1+1]和 [ l 2 + 1 , r 2 + 1 ] [l_2+1,r_2+1] [l2+1,r2+1]的距离(两个 i f if if即可)。
我们可以枚举计算的区间对的位置差
所以我们可以用 a n s i , j ans_{i,j} ansi,j表示区间 [ i , i + l − 1 ] [i,i+l-1] [i,i+l−1]有多少个与其距离为 j j j的区间。然后做个前缀和即可。
时空间复杂度都是 O ( n 2 ) O(n^2) O(n2)。我们发现空间复杂度并不能胜任,所以我们可以将 a n s i , j ans_{i,j} ansi,j的 j j j的意义变为有多少个与其距离为 k j k_j kj的区间。
然后空间复杂度 O ( n q ) O(nq) O(nq)
c o d e code code
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=11000;
int n,q,a[N],x[N],num[N],ans[N][110],L;
bool v[N];
int main()
{
freopen("lottery.in","r",stdin);
//freopen("lottery.out","w",stdout);
scanf("%d%d",&n,&L);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]);
scanf("%d",&q);
for(int i=1;i<=q;i++)
scanf("%d",&x[i]),v[x[i]+1]=1;
num[0]=1;
for(int i=1;i<=n;i++)
num[i]=num[i-1]+v[i];
for(int l=1;l<=n;l++){
int dis=0;
if(l+L>n) break;
for(int i=1;i<=L;i++)
if(a[i]!=a[i+l]) dis++;
ans[1][num[dis]]++;
ans[1+l][num[dis]]++;
for(int i=2;i<=n-L+1;i++){
int j=i+l;
if(j>n-L+1) break;
if(a[i-1]!=a[j-1]) dis--;
if(a[i+L-1]!=a[j+L-1]) dis++;
ans[i][num[dis]]++;
ans[j][num[dis]]++;
}
}
for(int i=1;i<=num[n];i++)
for(int j=1;j<=n;j++)
ans[j][i]+=ans[j][i-1];
for(int i=1;i<=q;i++){
for(int j=1;j<=n-L+1;j++)
printf("%d ",ans[j][num[x[i]]]);
putchar('\n');
}
}