1. 程式人生 > 實用技巧 >《演算法競賽進階指南》0x44分塊 莫隊演算法 小Z的襪子

《演算法競賽進階指南》0x44分塊 莫隊演算法 小Z的襪子

題目連結:https://www.acwing.com/problem/content/description/253/

題目給出一個序列和M次詢問,問從[L,R]區間中取出顏色相同的襪子的概率,實際上就是計算組合數,由於詢問的數量過於龐大,只能用離線的莫隊演算法進行求解。

對詢問進行分塊,先按照l進行排序,進行分塊之後對每個分塊中的部分按照r進行排序,這樣就使得一個塊中的詢問完成後右端點最多合計移動了N,左端點最多在sqrt(N)範圍內移動,所以可以在O(N*sqrt(N))時間內求出所有的詢問對應的值,在詢問中需要設定詢問的編號,因為之後需要對他進行排序。

程式碼:

#include<iostream>
#include
<algorithm> #include<cstdio> #include<string.h> #include<math.h> using namespace std; const int maxn = 50010; typedef long long ll; struct node{ int l,r,id; }query[maxn]; ll Ans[maxn][2]; int c[maxn],L[maxn],R[maxn]; int num[maxn]; int n,m; ll ans; bool cmp0(node& a,node& b){//
按照l進行排序 return a.l<b.l || (a.l==b.l && a.r<b.r); } bool cmp1(node& a,node& b){//塊內按照r進行排序 return a.r<b.r; } void work(int x,int c){ ans-=(ll)num[x]*(num[x]-1); num[x]+=c; ans+=(ll)num[x]*(num[x]-1); } ll gcd(ll a,ll b){ return b?gcd(b,a%b):a; } int main(){
int n,m; cin>>n>>m; for(int i=1;i<=n;i++)scanf("%d",&c[i]); for(int i=1;i<=m;i++){ scanf("%d%d",&query[i].l,&query[i].r); query[i].id=i; } sort(query+1,query+m+1,cmp0); int t=sqrt(m); for(int i=1;i<=t;i++){ L[i]=(i-1)*t+1; R[i]=i*t; } if(R[t]<m){ L[t+1]=R[t]+1; R[++t]=m; } for(int i=1;i<=t;i++){//分塊內部按照r遞增排序,使得每個分塊內的查詢時間複雜度是O(N) sort(query+L[i],query+R[i]+1,cmp1); } for(int i=1;i<=t;i++){ memset(num,0,sizeof(num)); ans=0; int l=query[L[i]].l,r=query[L[i]].r; for(int j=l;j<=r;j++)work(c[j],1);//每個塊中第一個單獨考慮 Ans[query[L[i]].id][0]=ans; Ans[query[L[i]].id][1]=(ll)(r-l)*(r-l+1); ll g=gcd(Ans[query[L[i]].id][0],Ans[query[L[i]].id][1]); if(l==r){//只要不是兩個都是0,gcd都不會是0 Ans[query[L[i]].id][0]=0; Ans[query[L[i]].id][1]=1; }else{ Ans[query[L[i]].id][0]/=g; Ans[query[L[i]].id][1]/=g; } for(int j=L[i]+1;j<=R[i];j++){ while(r<query[j].r)work(c[++r],1); while(r>query[j].r)work(c[r--],-1); while(l>query[j].l)work(c[--l],1); while(l<query[j].l)work(c[l++],-1); if(query[j].l==query[j].r){ Ans[query[j].id][0]=0; Ans[query[j].id][1]=1; }else{ Ans[query[j].id][0]=ans; Ans[query[j].id][1]=(ll)(r-l)*(r-l+1); ll g=gcd(Ans[query[j].id][0],Ans[query[j].id][1]); Ans[query[j].id][0]/=g; Ans[query[j].id][1]/=g; } } } for(int i=1;i<=m;i++){ printf("%lld/%lld\n",Ans[i][0],Ans[i][1]); } return 0; }