初學莫隊

在學習莫隊之前,我看了很多篇博客,大部分的博客講的我一頭霧水。

莫隊算法是用了分塊的思想,沒有學習分塊的同學先去學習一下分塊。 莫隊的查詢是離線的,將所有的詢問進行排序。

假如我們要訪問(L,R)區間的內容,下一次的要訪問(L ,R + 2),第三次要求訪問(L ,R + 1)。我們可以對這個詢問進行一次排序。(L,R),(L,R + 1),(L , R + 2)  ;

由於莫隊用的是l,r兩個指針去尋找區間,每次要麼是l + 1,l - 1或者r + 1,r-1。所以我們當對(L,R)區間的已經訪問過時,我們可以在O(1)的時間內得到(L -1 ,R),(L + 1,R),(L,R+ 1),(L, R - 1)的值。

 

用兩道例題來講解一下

這一道是BZOJ的3781 。題意是要求我們求出訪問區間內相同的數字個數的平方和。 如(1,1,2,3,3,3) 答案就爲 2^2 + 1^2 + 2^2

先分塊,然後將詢問按照 L所在的塊排序,R的值進行排序。這樣一來我們將詢問儘量做在了一個塊中,l與r指針不會太多的無效移動。

#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <bitset>
#define  LL long long
#define  ULL unsigned long long
#define mod 10007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
using namespace std;
const int maxn = 1e6 + 5;
int block,l[maxn],r[maxn],num,a[maxn],n,belong[maxn];
int now,sum[maxn],k;
int q[maxn],p[maxn];
struct node
{
    int l,r,id;
}KK[maxn];
bool cmp(node a,node b)
{
   if(belong[a.l] != belong[b.l]) return belong[a.l] < belong[b.l];
   else return a.r < b.r;
}
void build(){
  block = sqrt(n);
  num = n/block;if(n % block) num++;
  for(int i = 1; i <= num; i++) l[i] = (i - 1)*block + 1,r[i] = i * block;
  r[num] = n;
  for(int i = 1; i <= n; i++){
    belong[i] = (i - 1)/block + 1;
  }

}
void modify(int x,int t){
  //if(a[x] > k) return;
  now -= (sum[a[x]] * sum[a[x]]);//sum的含義是當前有多少個a【i】在區間內
  sum[a[x]] += t;
  now += sum[a[x]] * sum[a[x]];
}
int main()
{
    int m;
    scanf("%d%d%d",&n,&m,&k);
    for(int i = 1; i <= n; i++) scanf("%d",&a[i]);
    build();
    for(int i = 1; i <= m; i++){
        scanf("%d%d",&KK[i].l,&KK[i].r);
        KK[i].id = i;
    }
    sort(KK + 1, KK + m + 1,cmp);
    int L = 1,R = 0;
    now = 0;
    for(int i = 1; i <= m; i++){
       while(L < KK[i].l) modify(L++,-1); while(L > KK[i].l) modify(--L,1);
       while(R < KK[i].r) modify(++R,1); while(R > KK[i].r) modify(R--,-1);
       int k = KK[i].id;
       q[k] = now;
    }
    for(int i = 1; i <= m; i++){
      printf("%d\n",q[i]);
    }

    return 0;
}

 

第二道,小z的襪子HYSBZ - 2038 

 

作爲一個生活散漫的人,小Z每天早上都要耗費很久從一堆五顏六色的襪子中找出一雙來穿。終於有一天,小Z再也無法忍受這惱人的找襪子過程,於是他決定聽天由命……
具體來說,小Z把這N只襪子從1到N編號,然後從編號L到R(L 儘管小Z並不在意兩隻襪子是不是完整的一雙,甚至不在意兩隻襪子是否一左一右,他卻很在意襪子的顏色,畢竟穿兩隻不同色的襪子會很尷尬。
你的任務便是告訴小Z,他有多大的概率抽到兩隻顏色相同的襪子。當然,小Z希望這個概率儘量高,所以他可能會詢問多個(L,R)以方便自己選擇。

Input

輸入文件第一行包含兩個正整數N和M。N爲襪子的數量,M爲小Z所提的詢問的數量。接下來一行包含N個正整數Ci,其中Ci表示第i只襪子的顏色,相同的顏色用相同的數字表示。再接下來M行,每行兩個正整數L,R表示一個詢問。

Output

包含M行,對於每個詢問在一行中輸出分數A/B表示從該詢問的區間[L,R]中隨機抽出兩隻襪子顏色相同的概率。若該概率爲0則輸出0/1,否則輸出的A/B必須爲最簡分數。(詳見樣例)

Sample Input

6 4 1 2 3 3 3 2 2 6 1 3 3 5 1 6

Sample Output

2/5 0/1 1/1 4/15 【樣例解釋】 詢問1:共C(5,2)=10種可能,其中抽出兩個2有1種可能,抽出兩個3有3種可能,概率爲(1+3)/10=4/10=2/5。詢問2:共C(3,2)=3種可能,無法抽到顏色相同的襪子,概率爲0/3=0/1。詢問3:共C(3,2)=3種可能,均爲抽出兩個3,概率爲3/3=1/1。注:上述C(a, b)表示組合數,組合數C(a, b)等價於在a個不同的物品中選取b個的選取方案數。【數據規模和約定】 30%的數據中N,M ≤ 5000; 60%的數據中N,M ≤ 25000; 100%的數據中N,M ≤ 50000,1 ≤ L < R ≤ N,Ci ≤ N 。

與上題不一樣的就是這題求的是概率。 但是不難發現,我們選一雙顏色相同的概率和平方也有關係。

設:sum【i】爲當前顏色爲i的襪子有多少隻,n爲當前區間的所有襪子的總數。要選出一雙顏色相同的概率爲                  sum【i】 *(sum【i】 - 1)/n*(n - 1) = sum【i】 * sum【i】 - sum【i】/n * (n- 1)。對整個區間的sum數組而言

sum【L~R】^2 - (R - L + 1)/n * (n - 1)

 

 

#include <queue>
#include <cstdio>
#include <set>
#include <string>
#include <stack>
#include <cmath>
#include <climits>
#include <map>
#include <cstdlib>
#include <iostream>
#include <vector>
#include <algorithm>
#include <cstring>
#include <stdio.h>
#include <ctype.h>
#include <bitset>
#define  LL long long
#define  ULL unsigned long long
#define mod 10007
#define INF 0x7ffffff
#define mem(a,b) memset(a,b,sizeof(a))
#define MODD(a,b) (((a%b)+b)%b)
using namespace std;
const int maxn = 1e6 + 5;
int block,l[maxn],r[maxn],num,a[maxn],n,belong[maxn];
int now,sum[maxn];
LL q[maxn],p[maxn];
struct node
{
    int l,r,id;
}KK[maxn];
bool cmp(node a,node b)
{
   if(belong[a.l] != belong[b.l]) return belong[a.l] < belong[b.l];
   else return a.r < b.r;
}

LL gcd(LL x,LL y){ return (y)?gcd(y,x%y):x; }
void build(){
  block = sqrt(n);
  num = n/block;if(n % block) num++;
  for(int i = 1; i <= num; i++) l[i] = (i - 1)*block + 1,r[i] = i * block;
  r[num] = n;
  for(int i = 1; i <= n; i++){
    belong[i] = (i - 1)/block + 1;
  }

}
void modify(int x,int t){
  now += (LL)(sum[a[x]] << 1)*t + 1;
  sum[a[x]] += t;
}
int main()
{
    int m;
    while(~scanf("%d%d",&n,&m)){
      for(int i = 1; i <= n; i++) scanf("%lld",&a[i]);
      for(int i = 1; i <= m; i++) {scanf("%lld%lld",&KK[i].l,&KK[i].r); KK[i].id = i;}
      build();
      sort(KK + 1, KK + m + 1,cmp);
      int L = 1,R = 0;
      now = 0;
      for(int i = 1; i <= m; i++){
        while(L < KK[i].l) modify(L++,-1); while(L > KK[i].l) modify(--L,1);
        while(R < KK[i].r) modify(++R,1);  while(R > KK[i].r) modify(R--,-1);
        LL t = KK[i].r - KK[i].l + 1;
        int x = KK[i].id;
        p[x] = now - t; q[x] = t * (t - 1);
        t = gcd(q[x],p[x]); p[x]/=t;q[x]/=t;
      }
      for(int i = 1; i <= m; i++) printf("%lld/%lld\n",p[i],q[i]);
    }
    return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章