bzoj2038: [2009国家集训队]小Z的袜子(hose)

传送门:http://www.lydsy.com:808/JudgeOnline/problem.php?id=2038

思路:首先对于一个[l,r]的询问,设col[i]表示第i种颜色在这一区间内的个数,那么随机抽到相同一对的概率就是

∑C(col[i],2)/C(r-l+1,2)

然后有:∑(col[i]^2-col[i])/((r-l+1)*(r-l))

然后我们发现得到了[l,r]的答案后,我们可以O(1)地得到[l,r-1]和[l,r+1]和[l-1,r]和[l+1,r]的答案。

这样我们就可以用莫队算法了。

我们只要按一定顺序去计算答案,就可以得到较优的复杂度。

求出最小曼哈顿距离生成树,在沿着树边一个一个得到答案即可。

但是这很复杂....

于是就有了对l分块,先把询问排序,第一关键字是l所在的块,第二关键字是r的大小,按照排序后的顺序计算就可以使复杂度变为O(n^1.5)

(我怎么感觉这充满了暴力...)

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
const int maxn=50010;
using namespace std;
typedef long long ll;
int n,m,col[maxn],sz,pos[maxn],k;ll ans,s[maxn];
struct que{int l,r,id;ll a,b;}a[maxn];
ll sqr(ll a){return a*a;}
void update(int x,int add){ans-=sqr(s[col[x]]),s[col[x]]+=add,ans+=sqr(s[col[x]]);}

bool cmp1(que a,que b){ 
	if (pos[a.l]==pos[b.l]) return a.r<b.r; 
	return a.l<b.l; 
}
bool cmp2(que a,que b){return a.id<b.id;}

void work(){
	sort(a+1,a+1+m,cmp1);
    for (int i=1,l=1,r=0;i<=m;i++){
		for (;r<a[i].r;r++) update(r+1,1);
		for (;r>a[i].r;r--) update(r, -1);
		for (;l<a[i].l;l++) update(l, -1);
		for (;l>a[i].l;l--) update(l-1,1);
		if (a[i].l==a[i].r){a[i].a=1;continue;}
		a[i].a=ans;
	}
	sort(a+1,a+1+m,cmp2);
	for (int i=1;i<=m;i++) printf("%lld\n",a[i].a);
}
  
int main(){
	scanf("%d%d%d",&n,&m,&k);sz=(int)sqrt(n);
	for (int i=1;i<=n;i++) scanf("%d",&col[i]);
	for (int i=1;i<=n;i++) pos[i]=(i-1)/sz+1;
	for (int i=1;i<=m;i++) scanf("%d%d",&a[i].l,&a[i].r),a[i].id=i;
	work();
	return 0; 
} 



你可能感兴趣的:(莫队算法)