hdu 5381 2015多校第八場 莫隊算法


還沒學過莫隊算法。。。。網上也找不到莫隊算法的論文,只能勉強看着別人的代碼打下來。。。


稍微介紹下莫隊算法:

能使用莫隊算法的前提是這樣的--如果我們已知[l,r]的答案,能在O(1)時間得到[l + 1,r]的答案以及[l,r - 1]的答案,即可使用莫隊算法。時間複雜度爲O(n * sqrt(n))。如果求[l + 1,r]和[l,r - 1]要在O(logn)下完成,則時間複雜度是O(n * sqrt(n) * logn)。   PS:莫隊算法一般處理離線無修改的區間詢問

在轉移複雜度是O(1)的情況下,已知[l,r]要求[l',r']的答案,那麼我們可以在O(|l - l'| + |r - r'|)內求得。

莫隊在論文中使用了二維曼哈頓距離最小生成樹,而這個東西要用到區域劃分法+梳妝數組or線段樹維護(複雜度極值nlogn),並用kruskal在nlogn下求得,但是實現起來太麻煩了。。。


所以一般用一個比較簡單的方法--分塊。

將sqrt(n)區間內的點分在一塊當中,分爲sqrt(n)塊,按區間排序。以左端點所在塊內爲第一關鍵字,右端點爲第二關鍵字,進行排序,也就是以(pos[l],r)進行排序,然後複雜度就保證是O(nlogn)了。。。以下爲證明。。。。


一、i與i+1在同一塊內,r單調遞增,所以r是O(n)的。由於有n^0.5塊,所以這一部分時間複雜度是n^1.5。
二、i與i+1跨越一塊,r最多變化n,由於有n^0.5塊,所以這一部分時間複雜度是n^1.5
三、i與i+1在同一塊內時變化不超過n^0.5,跨越一塊也不會超過2*n^0.5,不妨看作是n^0.5。由於有n個數,所以時間複雜度是n^1.5
於是就變成了O(n^1.5)了




以下爲代碼:

試了一下,如果不加分塊的話必定超時,加了之後就是187ms,嚇cry。。。。

#include 
#define mem(a,b) memset(a,b,sizeof(a))
typedef long long ll;
using namespace std;
const int maxn = 100005;

int n,m,a[maxn],ppos[maxn];
long long ans[maxn];
int v1[maxn][33],v2[maxn][33],v3[maxn][33],v4[maxn][33];
int size1[maxn],size2[maxn];

struct Q{
    int l,r,id;
    bool operator <(const Q & b)const{
        if(ppos[l] == ppos[b.l])return r < b.r;
        return ppos[l] < ppos[b.l];
    }
    void read(int i){scanf("%d%d",&l,&r);id = i;}
}q[maxn];
int gcd(int a,int b){return !b ? a : gcd(b,a % b);}
int pool[50],pos[50],cnt;

void unique(int &cnt)
{
    int id = 0,p = pos[0];
    for(int i = 0;i < cnt;i++)
    {
        if(pool[i] != pool[id]){
            pos[id] = p;id++;p = pos[i];pool[id] = pool[i];
        }
    }
    pos[id] = p;
    cnt = id + 1;
}

void init()
{
    cnt = 0;
    for(int i = n;i >= 1;i--)
    {
        for(int j = 0;j < cnt;j++)pool[j] = gcd(pool[j],a[i]);
        pool[cnt] = a[i];pos[cnt++] = i;
        unique(cnt);
        for(int j = cnt - 1;j >= 0;j--)
        {
            v1[i][cnt - 1 - j] = pool[j];
            v2[i][cnt - 1 - j] = pos[j];
        }
        size1[i] = cnt;
    }
    cnt = 0;
    for(int i = 1;i <= n;i++)
    {
        for(int j = 0;j < cnt;j++)pool[j] = gcd(pool[j],a[i]);
        pool[cnt] = a[i];pos[cnt++] = i;
        unique(cnt);
        for(int j = cnt - 1;j >= 0;j--)
        {
            v3[i][cnt - 1 - j] = pool[j];
            v4[i][cnt - 1 - j] = pos[j];
        }
        size2[i] = cnt;
    }
}

int l,r;
ll sum;

void add_l(int v)
{
    int s = l,t = r;
    ll temp = 0;
    int last = s;
    for(int i = 0;i < size1[l];i++){
        if(v2[l][i] < s)continue;
        else if(v2[l][i] > t)
            temp += (t - last + 1) * 1LL * v1[l][i];
        else {
            temp += (v2[l][i] - last + 1) * 1LL * v1[l][i];
            last = v2[l][i] + 1;
        }
        if(v2[l][i] >= t)break;
    }
    sum += v * temp;
}
void add_r(int v)
{
    int s = l,t = r;
    ll temp = 0;
    int last = t;
    for(int i = 0;i < size2[r];i++){
        if(v4[r][i] > t)continue;
        else if(v4[r][i] < s)
            temp += (last - s + 1) * 1LL * v3[r][i];
        else {
            temp += (last - v4[r][i] + 1) * 1LL * v3[r][i];
            last = v4[r][i] - 1;
        }
        if(v4[t][i] <= s)break;
    }
    sum += v * temp;
}

int main()
{
    int T;
    scanf("%d",&T);
    for(int cas = 1;cas <= T;cas++)
    {
        scanf("%d",&n);
        for(int i = 1;i <= n;i++)scanf("%d",a + i);
        for(int i = 1;i <= n;i++)
            ppos[i] = (i - 1) / 100;
        init();
        scanf("%d",&m);
        for(int i = 0;i < m;i++)q[i].read(i);
        sort(q,q + m);
        sum = 0,l = 1,r = 0;
        for(int i = 0;i < m;i++)
        {
            if(r < q[i].r){
                for(r = r + 1;r <= q[i].r;r++)
                    add_r(1);
                r--;
            }
            if(r > q[i].r){
                for(;r > q[i].r;r--)
                    add_r(-1);
            }
            if(l > q[i].l){
                for(l = l - 1;l >= q[i].l;l--)
                    add_l(1);
                l++;
            }
            if(l < q[i].l){
                for(;l < q[i].l;l++)
                    add_l(-1);
            }
            ans[q[i].id] = sum;
        }
        for(int i = 0;i < m;i++)
            printf("%I64d\n",ans[i]);
    }
}

發佈了50 篇原創文章 · 獲贊 3 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章