传送门: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; }