1. 程式人生 > >#6164. 「美團 CodeM 初賽 Round A」數列互質-莫隊

#6164. 「美團 CodeM 初賽 Round A」數列互質-莫隊

#6164. 「美團 CodeM 初賽 Round A」數列互質

思路 : 對這個題來言,莫隊可以 n*根號n 離線處理出各個數出現個的次數 ,同時可以得到每個次數出現的次數 ,

但是還要處理有多少 次數 與ki互質 。根據數列的性質,無論這個區間多長,最長也就是 1 - n這個區間 ,所能產生的

不同的次數 也就是 根號 n 種  例如 長度為28的 數列    1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 不同的次數 只有 7種 

數列越長了 這個性質體現的越明顯, 原因就是 根據他們的出現的次數求個和 就算他們都按 最小的次數出現的話 1 2 3 4...s 求和為 (1+s)*s/2

而這個數又不能超過 區間長度 ,所以 這個 s也就在 根號 長度左右。 所以可以放心的進行暴力求解,有插入刪除操作,可以陣列模擬連結串列實現。

#include<bits/stdc++.h>
using namespace std;
#define maxn 55555
int n,m,cnt[maxn*2],tong[maxn*2],block,ans[maxn],data[maxn];
int nxt[maxn*2],lst[maxn*2],head=0,tail=maxn-1,l,r;
struct node
{
    int l,r,k,id;
    bool operator <(const node &b)const
    {
        return l/block==b.l/block?r<b.r:l<b.l;
    }
} a[maxn];
void upda(int rd)
{
    if(rd<=head)return;
    nxt[rd]=nxt[head];
    nxt[head]=rd;
    lst[rd]=head;
    lst[nxt[rd]]=rd;
}
void rmove(int rd)
{
    if(rd<=head)return;
    nxt[lst[rd]]=nxt[rd];
    lst[nxt[rd]]=lst[rd];
}
void ins(int x)
{
    if(--tong[cnt[x]]==0)rmove(cnt[x]);
    if(++tong[++cnt[x]]==1)upda(cnt[x]);
}
void del(int x)
{
    if(--tong[cnt[x]]==0)rmove(cnt[x]);
    if(++tong[--cnt[x]]==1)upda(cnt[x]);
}
int main()
{
    scanf("%d%d",&n,&m);
    block=sqrt(n);
    nxt[0]=tail;
    lst[tail]=0;
    for(int i=1; i<=n; i++)
        scanf("%d",&data[i]);
    for(int i=0; i<m; i++)
    {
        a[i].id=i;
        scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].k);
    }
    sort(a,a+m);
    l=a[0].l;
    r=a[0].l-1;
    for(int i=0; i<m; i++)
    {
        while(l>a[i].l)
            ins(data[--l]);
        while(l<a[i].l)
            del(data[l++]);
        while(r>a[i].r)
            del(data[r--]);
        while(r<a[i].r)
            ins(data[++r]);
        int cp=0;
        for(int j=nxt[head]; j!=tail; j=nxt[j])
            if(__gcd(a[i].k,j)==1) cp+=tong[j];
        ans[a[i].id]=cp;
    }
    for(int i=0; i<m; i++)printf("%d\n",ans[i]);
    return 0;
}