莫涛大神发明的似乎可解一切区间问题的算法,本蒟蒻觉得似乎其本质是分块优化再加上多关键字排序。分块大法好
拿题说事
这是道很好拿来理解莫队的题
题目大意
进行区间询问[l,r],输出该区间内随机抽两次抽到相同颜色袜子的概率。
分析
当你确定区间[l,r]的答案时(分子为各种颜色的平方和,分母为(l-r)^2),可借其进行分析,当区间变为[l-1,r]时,设l块颜色为co(原来的贡献为x),则分母变为(l-r-1)^2,分母减去x^2+(x-1)^2,可总结出当该变量为1时,可O(1)进行修改,然而这还是不够的,毕竟在最坏条件下还是O(n^2),这时就需要神奇的排序了,先将序列分为根号n段,每块大为k,在排序时,若在同一段则按r排序,否则按l排序,即将l作为第一关键字,r作为第二关键字进行排序,对于l来说,最多经过2*k即可完成目标,复杂度为:O(m*k),对于r来说,只有在同一段里时才有序,而对于每个块来说,最多移动n次,一共n/k个块,故复杂度为O(n*n/k),所以总复杂度为O(n*sqrt(n)),实际为O(玄学)反正能过就行
上代码
#include<bits/stdc++.h>
#define mem(a,b) memset(a,b,sizeof(a))
#define ll long long
using namespace std;
struct date{
int l,r,ID;
ll A,B;
}q[50003];
ll S(ll x){ return x*x;}
ll GCD(ll a,ll b){
return b==0?a:GCD(b,a%b);
}
long long n,m,col[50003],unit,x[50003];
ll sum[50003],ans;
bool cmp(date a,date b){
return x[a.l]==x[b.l]?a.r<b.r:a.l<b.l;
}
bool CMP(date a,date b){
return a.ID<b.ID;
}
void cl(int x,int add){
ans-=S(sum[col[x]]);
sum[col[x]]+=add;
ans+=S(sum[col[x]]);
}
int main()
{
scanf("%d%d",&n,&m);
unit=sqrt(n);
for(int i=1;i<=n;i++) scanf("%d",&col[i]),x[i]=i/unit+1;;
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].ID=i;
sort(q+1,q+m+1,cmp);
int l=1,r=0;
for(int i=1;i<=m;i++)
{
while(l<q[i].l) cl(l,-1),l++;
while(l>q[i].l) cl(l-1,1),l--;
while(r<q[i].r) cl(r+1,1),r++;
while(r>q[i].r) cl(r,-1),r--;
if(q[i].l==q[i].r){
q[i].A=0,q[i].B=1;
continue;
}
q[i].A=ans-q[i].r+q[i].l-1;
q[i].B=1LL*(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
ll gcd=GCD(q[i].A,q[i].B);
q[i].A/=gcd;
q[i].B/=gcd;
}
sort(q+1,q+m+1,CMP);
for(int i=1;i<=m;i++) printf("%lld/%lld\n",q[i].A,q[i].B);
return 0;
}