正题
题目大意
给出一个长度为nnn的序列aaa和数字lll,定义两个长度为lll的区间[l1,r1][l_1,r_1][l1,r1]和[l2,r2][l_2,r_2][l2,r2]的距离为有多少个不相同的数字。
然后有qqq个询问kik_iki,要求输出有多少对距离为kik_iki的区间。
解题思路
我们发现若我们求出了[l1,r1][l_1,r_1][l1,r1]和[l2,r2][l_2,r_2][l2,r2]的距离,就可以O(1)O(1)O(1)求出[l1+1,r1+1][l_1+1,r_1+1][l1+1,r1+1]和[l2+1,r2+1][l_2+1,r_2+1][l2+1,r2+1]的距离(两个ififif即可)。
我们可以枚举计算的区间对的位置差
所以我们可以用ansi,jans_{i,j}ansi,j表示区间[i,i+l−1][i,i+l-1][i,i+l−1]有多少个与其距离为jjj的区间。然后做个前缀和即可。
时空间复杂度都是O(n2)O(n^2)O(n2)。我们发现空间复杂度并不能胜任,所以我们可以将ansi,jans_{i,j}ansi,j的jjj的意义变为有多少个与其距离为kjk_jkj的区间。
然后空间复杂度O(nq)O(nq)O(nq)
codecodecode
#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');}
}