天天看點

hdu 5145(莫隊算法+逆元)

NPY and girls

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)

Total Submission(s): 790    Accepted Submission(s): 280

Problem Description

NPY's

girlfriend blew him out!His honey doesn't love him any more!However, he

has so many girlfriend candidates.Because there are too many girls and

for the convenience of management, NPY numbered the girls from 1 to

n.These girls are in different classes(some girls may be in the same

class).And the i-th girl is in class ai.NPY wants to visit his girls

frequently.Each time he visits some girls numbered consecutively from L

to R in some order. He can only visit one girl every time he goes into a

classroom,otherwise the girls may fight with each other(-_-!).And he

can visit the class in any order.

Here comes the problem,(NPY doesn't

want to learn how to use excavator),he wonders how many different ways

there can be in which he can visit his girls.The different ways are

different means he visits these classrooms in different order.

Input

The first line contains the number of test cases T(1≤T≤10).

For each test case,there are two integers n,m(0<n,m≤30000) in the first line.N is the number of girls,and M is the number of times that NPY want to visit his girls.

The following single line contains N integers, a1,a2,a3,…,an, which indicates the class number of each girl. (0<ai≤30000)

The following m lines,each line contains two integers l,r(1≤l≤r≤n),which indicates the interval NPY wants to visit.

Output

For each visit,print how many ways can NPY visit his girls.Because the ans may be too large,print the ans mod 1000000007.

Sample Input

2

4 2

1 2 1 3

1 3

1 4

1 1

1

Sample Output

3

12

Source

BestCoder Round #22

題意:有n個班級,詢問在[l,r]區間内班級女生的排列方式有多少種,比如說 1 1 2,那麼就有三種 1 1 2 ,2 1 1,1 2 1。

題解:首次接觸莫隊算法。

莫隊算法:可以解決一類靜态,離線區間查詢問題。http://blog.csdn.net/bossup/article/details/39236275

發明這個的人好強啊,沒看懂分塊,不過用起來的話還是會用模闆的.[l,r]裡面的數字假設有m個,每個班級有c[i]個人,那麼排列組合的總數為 m!/ ( c[1]! * .......c[n]! ) 關鍵是怎麼用莫對算法求解這個式子。當區間從 [l,r-1] - >[l,r]時,那麼假設之前的區間[l,r-1]裡面的排列數為 N1,那麼[l,r]的結果應當為 N1*(r+l-1)/c[a[r]++] ,反之,當現在的區間從

[l,r+1]->[l,r]時,假設 [l,r+1]裡面的排列數為 N2,那麼目前[l,r]裡面的排列數應該變成了 N2*c[a[r+1]++]/(r-l+1)個人.還有左邊區間兩種情況可以仿造這兩種情況得出來。

由于有取模操作,是以除法要用上逆元.

#include<stdio.h>
#include<iostream>
#include<string.h>
#include <stdlib.h>
#include<math.h>
#include<algorithm>
using namespace std;
typedef long long LL;
const LL mod = 1000000007;
const int N = 30005;
int n,m;
LL a[N];
LL inv[N];
LL Hash[N];
LL res[N];
struct Node{
    int l,r,sqr,id;
}node[N];
int cmp(Node a,Node b){
    if(a.sqr==b.sqr) return a.r<b.r;
    return a.l<b.l;
}
LL pow_mod(LL a,LL n){
    LL ans = 1;
    while(n){
        if(n&1) ans = ans*a%mod;
        a = a*a%mod;
        n>>=1;
    }
    return ans;
}
LL getinv(int x){
    return pow_mod(x,mod-2);
}
int main()
{
    int tcase;
    scanf("%d",&tcase);
    for(int i=1;i<=N;i++){
        inv[i] = getinv(i);
    }
    while(tcase--)
    {
        scanf("%d%d",&n,&m);
        int k = (int)sqrt(n);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        for(int i=1;i<=m;i++){
            scanf("%d%d",&node[i].l,&node[i].r);
            node[i].id = i;
            node[i].sqr = node[i].l/k;
        }
        sort(node+1,node+m+1,cmp);
        memset(Hash,0,sizeof(Hash));
        int l=1,r = 1;
        Hash[a[1]]++;
        LL ans = 1;
        for(int i=1;i<=m;i++){
            while(r<node[i].r){
                r++;
                Hash[a[r]]++;
                ans = ans*(r-l+1)%mod*inv[Hash[a[r]]]%mod;
            }
            while(l>node[i].l){
                l--;
                Hash[a[l]]++;
                ans = ans*(r-l+1)%mod*inv[Hash[a[l]]]%mod;
            }
            while(r>node[i].r){
                ans = ans*Hash[a[r]]%mod*inv[r-l+1]%mod;
                Hash[a[r]]--;
                r--;
            }
            while(l<node[i].l){
                ans = ans*Hash[a[l]]%mod*inv[r-l+1]%mod;
                Hash[a[l]]--;
                l++;
            }
            res[node[i].id] = ans;
        }
        for(int i=1;i<=m;i++){
            printf("%lld\n",res[i]);
        }
    }
    return 0;
}