[xsy1155]作业判重

题意:先定义两个序列$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)));
	}
}

猜你喜欢

转载自www.cnblogs.com/jefflyy/p/9216677.html