题意:先定义两个序列$A,B$的相似度为$\dfrac{\sum cntA_icntB_i}{\sqrt{\left(\sum cntA_i^2\right)\left(\sum cntB_i^2\right)}}$,其中$cntA_i$表示$i$在$A$中出现次数,类似地定义$cntB$,给出一个序列,多次询问序列中两个相邻子串的相似度,只要有$75\%$的回答与标准答案的绝对误差$\leq0.15$即为正确
允许有较大的误差,可以往随机化方面考虑
首先我们考虑将所有数字映射到$\{1,-1\}$上,相同的数字映射到同一个数,设$f_i$表示$i$映射到的数
$\left(\sum\limits cntA_if_i\right)^2$的期望是$\sum cntA_i^2$(把期望的式子写出来,将平方展开后所有的$cntA_icntA_j$都互相抵消了,剩下平方的项)
$\left(\sum cntA_if_i\right)\left(\sum cntB_if_i\right)$的期望是$\sum\limits cntA_icntB_i$(同样地,展开后所有的$cntA_icntB_j$互相抵消)
于是我们可以映射许多次,求出平均值用于近似即可,又因为我们要求的$\sum cntA_if_i$和$\sum cntB_if_i$都是线性的,所以可以求前缀和
误差分析要用到切比雪夫不等式,可惜我的概率论仍然是小学生水平,这个坑以后再填吧...
#include<stdio.h> #include<stdlib.h> #include<time.h> #include<math.h> const int M=100; int max(int a,int b){return a>b?a:b;} struct vec{ float x[M]; }s[1000010],v[100010]; vec operator+(vec a,vec b){ vec c; for(int i=0;i<M;i++)c.x[i]=a.x[i]+b.x[i]; return c; } vec operator-(vec a,vec b){ vec c; for(int i=0;i<M;i++)c.x[i]=a.x[i]-b.x[i]; return c; } float operator*(vec a,vec b){ float s=0; for(int i=0;i<M;i++)s+=a.x[i]*b.x[i]; return s; } int a[1000010],mp[100010]; int main(){ srand(time(0)); int n,q,i,j,T,l,m,r; vec sl,sr; scanf("%d",&n); T=0; for(i=1;i<=n;i++){ scanf("%d",a+i); T=max(T,a[i]); } for(i=1;i<=T;i++){ for(j=0;j<M;j++)v[i].x[j]=(rand()&1)?1:-1; } for(i=1;i<=n;i++)s[i]=s[i-1]+v[a[i]]; scanf("%d",&q); while(q--){ scanf("%d%d%d",&l,&m,&r); sl=s[m]-s[l-1]; sr=s[r]-s[m]; printf("%.3lf\n",sl*sr/sqrt((sl*sl)*(sr*sr))); } }