标签:关键字 ++i code clu 复杂 两个指针 gcd std toc
目录
博客安利:
解决一类离线区间查询问题,分块思想,时间复杂度\(O(n\sqrt n)\)
排序
读入的时候对整个数组进行分块,块大小一般使用\(\sqrt n\),对询问操作排序的时候,先以块号为第一关键字,\(r\)为第二关键字,从小到大排序,然后逐个遍历
离线后将询问排序,顺序处理每个询问,暴力从上一个区间的答案转移到下一个区间的答案(通过两个指针的\(++\)和\(--\)操作实现)
指针移动
当\(l\)指针从绿色区域向右移动一格,更新操作对应,先减去cnt[绿色]的共享,cnt[绿色]\(--\),再加上cnt[绿色]区域的贡献,其他指针移动操作类似。
给出n只袜子颜色,每次给个区间\([l,r]\),询问这个区间连续取两只袜子取到同一种颜色的概率。
思路:
对于L,R的询问。
设其中颜色为x,y,z的袜子的个数为a,b,c..…
那么答案即为 \((a*(a-1)/2+b*(b-1)/2+c*(c-1)/2....)/((R-L+1)*(R-L)/2)\)
化简得: \((a^2+b^2+c^2+...x^2-(a+b+c+d+.....))/((R-L+1)*(R-L))\)
即: \((a^2+b^2+c^2+...x^2-(R-L+1))/((R-L+1)*(R-L))\)
我们需要解决的一个问题是:求一个区间内每种颜色数目的平方和。
AcCode:
#include <iostream>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long LL;
const int maxn=5e4+5;
//存储询问区间
struct node{int l,r,id;LL A,B;}q[maxn];
//in[]存储每个数所在的区间
int n,m,a[maxn],in[maxn];
//处理前预排序
inline bool cmp(node& a,node& b){
return in[a.l]==in[b.l]?a.r<b.r:a.l<b.l;
}
//处理完,返回原来询问的顺序
inline bool CMP(node& a,node& b){return a.id<b.id;}
//cnt[]存储每个数的个数
int ans,cnt[maxn];
LL pow(LL x){return x*x;}
inline void update(int x,int d){
ans-=pow(cnt[a[x]]),cnt[a[x]]+=d,ans+=pow(cnt[a[x]]);
}
inline void solve(){
int l=1,r=0;
for(int i=1;i<=m;++i){
while(l<q[i].l)update(l,-1),l++;
while(l>q[i].l)update(l-1,1),l--;
while(r<q[i].r)update(r+1,1),r++;
while(r>q[i].r)update(r,-1),r--;
if(l==r){q[i].A=0,q[i].B=1;continue;}
q[i].A=ans-(r-l+1);
q[i].B=1LL*(r-l+1)*(r-l);
LL gc=__gcd(q[i].A,q[i].B);
q[i].A/=gc,q[i].B/=gc;
}
}
int main(){
cin>>n>>m;
int sqr=sqrt(n);
for(int i=1;i<=n;++i)cin>>a[i],in[i]=i/sqr+1;
for(int i=1;i<=m;++i)cin>>q[i].l>>q[i].r,q[i].id=i;
sort(q+1,q+1+m,cmp);
solve();
sort(q+1,q+1+m,CMP);
for(int i=1;i<=m;++i)
printf("%lld/%lld\n",q[i].A,q[i].B);
return 0;
}
标签:关键字 ++i code clu 复杂 两个指针 gcd std toc
原文地址:https://www.cnblogs.com/sstealer/p/11703307.html