小Z的襪子(莫隊基礎)

小Z的襪子

我的莫隊之旅開始啦!

題意:求區間[l,r]中相同數字的數量關係(具體見題)

思路:(莫隊思路)

  1. 將所有詢問按照左端點ll所在塊進行排序,若左端點屬於同一塊,則按照右端點排序(不用按照左端點具體大小排序啦!)
  2. 排序的一點優化,爲後面求解過程加速:對於左端點屬於第奇數塊的詢問,將它們按照右端點從小到大排序;對於左端點屬於第偶數塊的詢問,將它們按照右端點,從大到小排序。這樣相反的排序可以讓rr指針不用每次都從最右邊跑到最左邊。
  3. 莫隊的四個whilewhile循環,暴力的模擬l,rl,r指針逐漸靠近q.l,q.rq.l,q.r的過程,每次移動考慮插入和刪除。

題面描述

#include "bits/stdc++.h"
#define hhh printf("hhh\n")
#define see(x) (cerr<<(#x)<<'='<<(x)<<endl)
using namespace std;
typedef long long ll;
typedef pair<int,int> pr;
inline int read() {int x=0;char c=getchar();while(c<'0'||c>'9')c=getchar();while(c>='0'&&c<='9')x=x*10+c-'0',c=getchar();return x;}

const int maxn = 5e4+10;
const int inf = 0x3f3f3f3f;
const int mod = 1e9+7;
const double eps = 1e-7;

int n, m, len;
int c[maxn], cnt[maxn];
ll ans1[maxn], ans2[maxn], ans;

struct Q{
    int l, r, id;
    friend bool operator < (const Q &a, const Q &b) {
        if((a.l-1)/len==(b.l-1)/len) {
            if((a.l-1)/len%2) return a.r>b.r;
            else return a.r<b.r;
        }
        return a.l<b.l;
    }
}q[maxn];

inline void update(int p, int f) {
    ans-=1ll*cnt[c[p]]*(cnt[c[p]]-1);
    cnt[c[p]]+=f;
    ans+=1ll*cnt[c[p]]*(cnt[c[p]]-1);
}

ll gcd(ll a, ll b) { return b==0?a:gcd(b,a%b); }

int main() {
    //ios::sync_with_stdio(false); cin.tie(0);
    n=read(), m=read(); len=sqrt(n);
    for(int i=1; i<=n; ++i) c[i]=read();
    for(int i=1; i<=m; ++i) q[i]=(Q){read(),read(),i};
    sort(q+1,q+1+m);
    int l=1, r=0;
    int cnt=0;
    for(int i=1; i<=m; ++i) {
        while(l<q[i].l) update(l++,-1);
        while(l>q[i].l) update(--l,1);
        while(r<q[i].r) update(++r,1);
        while(r>q[i].r) update(r--,-1);
        int j=q[i].id;
        if(q[i].l==q[i].r) { ans1[j]=0; ans2[j]=1; continue; }
        ans1[j]=ans; ans2[j]=1ll*(q[i].r-q[i].l+1)*(q[i].r-q[i].l);
        ll d=gcd(ans1[j],ans2[j]);
        ans1[j]/=d; ans2[j]/=d;
    }
    for(int i=1; i<=m; ++i) printf("%lld/%lld\n", ans1[i], ans2[i]);
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章