1. 程式人生 > >hdu 5145 莫隊演算法模板題

hdu 5145 莫隊演算法模板題

題目:

題目分析:

本題是沒有修改操作的區間查詢,在計算時很容易推出公式,設當前元素個數為N,

那麼對於一個區間的方案數就是 n!/ ( sum[a1]! * .......sum[ax]! ) , a1.......ax是選取區間內互不相等的元素

運用到了排列組合的知識,就是所有的元素隨機排列,然後排除相同元素內部排列的情況,因為做的運算是乘除法,具有結合性和可逆性,所以可以利用莫隊演算法進行遞推的實現,由於有除法的取模運算,所以要把逆元預處理出來,然後存到數組裡o(1)的詢問,因為要取模的是一個大質數所以我們可以利用費馬小定理求逆元,當然拓展歐幾里得也是可以的,具體實現是通過取平方根之後分組進行類似於遞推的莫隊演算法實現,具體演算法程式碼中有詳細的註釋

程式碼:

#include <iostream>
#include <cstring>
#include <algorithm>
#include <cstdio>
#include <cmath>
#define MAX 30009
#define MOD 1000000007

using namespace std;
ya
long long inv[MAX];
int n , m;
int a[MAX];
int cnt[MAX];
long long  output[MAX];
//通過費馬小定理求取逆元,log的演算法,
//由費馬小定理可知,a對於m的逆元公式= a^(m-2)%m
//我們可以利用二進位制的性質將線性運算優化為log
//也就是將指數化作二進位制數,從低到高按照數位進行
long long  get_inv ( long long x , long long m )
{
    long long r , y;
    for ( r = 1 , y = m-2 ; y ; x = x * x %MOD , y >>= 1 )
        ( y & 1 ) && ( r = r * x %MOD );
    return r;
}
//預處理,求出所有可能用到的逆元
void pre ( )
{
    inv[0] = 1;
    for ( int i = 1 ; i < MAX ; i++ )
        inv[i] = get_inv ( i , MOD );
}
//用於分組的,為了保證複雜度是sqrt(n)n,所以選取平方根為分組的界限
int Sqrt;

//查詢數對,先按組別排序,相同組別的按右邊界進行排序
struct query
{
    int l,r,id;
    bool operator < ( const query& a ) const
    {
        return ( l/Sqrt < a.l/Sqrt ) || ( l/Sqrt == a.l/Sqrt && r < a.r );
    } 

}q[MAX];

void solve ( )
{
    Sqrt = sqrt ( n * 1.0 );
    sort ( q , q + m );
    memset ( cnt , 0 , sizeof ( cnt ) );
    long long L =1 , R = 1 , tot = 1 , ans = 1;
    //記錄當前點的總數,當前答案的左右邊界
    cnt[a[1]]++;
    //因為已經按組別分好,所以左邊界的修正範圍在sqrt(n)之內,所以在每一位最多的轉移是sqrt(n)
    for (  int i = 0 ; i < m ; i++ )
    {
        //根據乘除法運算的可逆性和結合律,對當前答案進行修正轉移,直至當前答案的左右邊界與查詢對重合,
       //而提前的分組和排序處理使只有在組別切換時才會出現右邊界的回溯,同組進行時只會出現左邊界的回走,而每組的長度是sqrt,所以能夠達到一定的優化使總的複雜度
      //控制在n*sqrt (n)左右
        while ( R < q[i].r )
        {
            tot++,R++,cnt[a[R]]++;
            ans = 1LL * ans * inv[cnt[a[R]]] %MOD;
            ans = 1LL * ans * tot % MOD;
        }
        while ( R > q[i].r )
        {
            ans = 1LL * ans * inv [tot] % MOD;
            ans = 1LL * ans * cnt[a[R]] % MOD;
            tot--,cnt[a[R]]--,R--;
        }
        while ( L < q[i].l )
        {
            ans = 1LL * ans * inv[tot] % MOD;
            ans = 1LL * ans * cnt[a[L]] % MOD;
            tot--,cnt[a[L]]--,L++;
        }
        while ( L > q[i].l )
        {
            tot++,L--,cnt[a[L]]++;
            ans = 1LL * ans * inv[cnt[a[L]]]%MOD;
            ans = 1LL * ans * tot%MOD;
        }
        output[q[i].id] = ans;
    }
}

int main ( )
{
    pre ();
    int t;
    scanf ( "%d" , &t );
    while ( t-- )
    {
        scanf ( "%d%d" , &n , &m );
        for ( int i = 1 ; i <= n ; i ++ )
            scanf ( "%d" , &a[i] );
        for ( int i = 0 ; i < m ; i ++ )
        {
            scanf ( "%d%d" , &q[i].l , &q[i].r );
            q[i].id = i; 
        }
       solve ();
       for ( int i = 0 ; i < m ; i++ ) printf ( "%lld\n" , output[i] ); 
    }
}

相關推薦

hdu 5145 演算法模板

題目: 題目分析: 本題是沒有修改操作的區間查詢,在計算時很容易推出公式,設當前元素個數為N, 那麼對於一個區間的方案數就是 n!/ ( sum[a1]! * .......sum[ax]! ) , a1.......ax是選取區間內互不相等的元素 運用到了排列組合的知識

演算法模板以及簡單的入門總結

莫隊模板 //莫隊演算法主要處理離線問題,查詢只給出L,R //當[L,R]很容易向[L-1,R],[L+1,R],[L,R-1],[L,R+1]轉移時可用莫隊 //注意轉移的時候先擴張再收縮,L先向右,L再向左,最後再收縮 //add就是當前區間新增某元

模板】帶修改模板:洛谷P1903數顏色)

帶修改莫隊講解 ~閱前提示: 擁有普通莫隊的基礎知識;理解莫隊的思想; ~簡介: 莫隊支援的是離線操作,普通莫隊只支援查詢操作; 而帶修改莫隊還支援單點修改操作。 ~原理: 普通莫隊每一個詢問有L,R,ID三個屬性;因為只有查詢操作,所以改變其查詢順序並不會影響演算法

hdu 4358(演算法+dfs序列)

解題思路:用dfs求出整棵樹的dfs序列,這樣以u為根節點的子樹就轉化到相對應的區間上了。由於是區間不修改查詢問題,這個時候就可以用莫隊演算法了。 #pragma comment(linker, "/STACK:16777216") #include<iost

HDU 2544 最短路(dijkstra演算法模板

  Problem Description 在每年的校賽裡,所有進入決賽的同學都會獲得一件很漂亮的t-shirt。但是每當我們的工作人員把上百件的衣服從商店運回到賽場的時候,卻是非常累的!所以現在他們想要尋找最短的從商店到賽場的路線,你可以幫助他們嗎?

演算法入門 + 模板 Codeforces 617E

題意已知一個長度為n的數列 (0 ≤ ai ≤ 1 000 000) ,給m個區間,問每個區間有多少個子區間xor和為k  (1 ≤ n, m ≤ 100 000, 0 ≤ k ≤ 1 000 000) 莫隊演算法  如果你知道了[L,R]的答案。你可以在O(1)的時間下得

HDU 2255】【KM演算法模板+KM演算法詳解】 奔小康賺大錢

描述: 奔小康賺大錢 Time Limit: 1000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 7453    Accepted S

模板演算法

題意:給定一個大小為N的陣列,陣列中所有元素的大小<=N。你需要回答M個查詢。每個查詢的形式是L,R,K。你需要回答在範圍[ L,R ]中至少重複K次的數字的個數。N,M<=100000 誒,這題卡了好久,TLE,中間棄了一段,然後今天學弟學莫隊,

hdu 1711 KMP演算法模板

題意:給你兩個串,問你第二個串是從第一個串的什麼位置開始完全匹配的? kmp裸題,複雜度O(n+m)。 當一個字串以0為起始下標時,next[i]可以描述為"不為自身的最大首尾重複子串長度"。 當發生

HDU 4358 Boring counting 演算法

題目大意: 就是現在給出一個有N個結點的樹(N <= 100000)編號從1到N, 樹的根節點為1, 給定K, 每個點都有一個權值, 權值0 <= wi <= 10^9, 現在有Q次詢問, 對於每次詢問給出一個x代表詢問在編號x的結點及其子樹中, 出現恰好

HDU 4638 算法

total n) desc align struct hose nbsp reat some Group Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 32768/32768 K (Java/Others)

HDU 5521.Meeting 最短路模板

tput different 亞洲 key rate cstring 重現 not divide Meeting Time Limit: 12000/6000 MS (Java/Others) Memory Limit: 262144/262144 K (Java/

Newcoder 39 F.重排的迴文串(演算法+位運算)

Description 給一個長為 n n n 的只含小寫字母的字串 每次查詢一個區間$ [l

Newcoder 40 F.珂朵莉的約數(數論+演算法

Description 珂朵莉給你一個長為 n n n的序列,有

Newcoder 58 F.序列查詢(演算法+分塊+連結串列)

Description 給你一個序列 a a a,有

「知識學習&日常訓練」演算法(一)(Codeforce Round #340 Div.2 E)

題意 已知一個長度為\(n\)的整數數列\(a[1],a[2],…,a[n]\),給定查詢引數\(l,r\),問\([l,r]\)內,有多少連續子段滿足異或和等於\(k\)。 也就是說,對於所有的\(x,y (l\le x\le y\le r)\),能夠滿足\(a[x]\oplus a[x+1]\oplus

「日常訓練&知識學習」演算法(二):樹上(Count on a tree II,SPOJ COT2)

題意與分析 題意是這樣的,給定一顆節點有權值的樹,然後給若干個詢問,每次詢問讓你找出一條鏈上有多少個不同權值。 寫這題之前要參看我的三個blog:CFR326D2E、CFR340D2E和HYSBZ-1086,然後再看這幾個Blog—— 參考A:https://blog.sengxian.com/algori

hdu 4857 反向拓撲模板,入門

之前寫過的部落格,今天回頭看時,發現格式好亂,重新寫一下。    --------------------------------------- 分析 首先先處理特殊關係,對於有要求的數字,在他們倆之間建立一條邊,a->b 同時b的入度++,表示b的限制條件

淺談普通演算法

前言 對於一個維護區間的問題,最暴力的方法就是每次列舉區間,進行統計。 而這就是莫隊的基本思路 但不過莫隊的列舉是進行優化的,可以優化到\(O(N\sqrt{N})\) 基本思路 首先:已知\([L,R]\)的答案,那麼求\([L-1,R]\) 、\([L+1,R]\) 、\([

要成為魔法少女嗎!!1681(二分圖匹配——匈牙利演算法模板

題目描述 酸奶醬是一位魔法少女,並且她很熱衷於點化她的其他小夥伴和她一起成為魔法少女。 現在有一個棘手的問題擺在酸奶醬面前——她有M套成為魔法少女不可缺少的魔法戰鬥服,以及N個想成為魔法少女的小夥伴。魔法戰鬥服是有靈性的,它有想要跟隨的主人。酸奶醬想盡可能多的把更多的魔法戰鬥服分給她的小夥伴,她現在想知道