【莫队算法】[HDU5145]NPY and girls

题目
题目大意:求区间内可重复全排列的个数
分析:如果S是一个多重集,它有K个不同的类型元素,各元素分别为n1,n2,…,nk个,那么,S的r排列个数为n! / (n1!n2!…*nr!)。
所以当我们知道区间[l,r]的答案求[l,r+1]的答案时,ans[l][r+1]=ans[l][r]*((r+1)-l+1)/cnt[a[r+1]];
由于模运算不支持除法,可以用乘以这个数的逆元来代替除法。
同时因为MOD是质数,可以用费马小定理快速求乘法逆元。

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
using namespace std;
#define MAXN 30000
#define MOD 1000000007
int n,m,block[MAXN+10],inv[MAXN+10],T,cnt[MAXN+10],ansq[MAXN+10],a[MAXN+10];
void Read(int &x){
    char c;
    while(c=getchar(),c!=EOF)
        if(c>='0'&&c<='9'){
            x=c-'0';
            while(c=getchar(),c>='0'&&c<='9')
                x=x*10+c-'0';
            ungetc(c,stdin);
            return;
        }
}
struct node{
    int l,r,pos;
    bool operator<(const node &x)const{
        if(block[l]==block[x.l])
            return r<x.r;
        return block[l]<block[x.l];
    }
}query[MAXN+10];
int quick_pow(int a,int b){
    int ret=1;
    while(b){
        if(b&1)
            ret=1ll*ret*a%MOD;
        a=1ll*a*a%MOD;
        b>>=1;
    }
    return ret;
}
void prepare(){
    for(int i=1;i<=MAXN;i++)
        inv[i]=quick_pow(i,MOD-2);
}
void init(){
    Read(n),Read(m);
    int i,t=sqrt(n+0.5);
    for(i=1;i<=n;i++){
        block[i]=(i+t-1)/t;
        Read(a[i]);
    }
    node *p;
    for(i=1;i<=m;i++){
        p=&query[i];
        Read(p->l),Read(p->r);
        p->pos=i;
    }
    sort(query+1,query+m+1);
}
void solve(){
    int i,l=1,r=0,ans=1;
    node *p;
    for(i=1;i<=m;i++){
        p=&query[i];
        while(r<p->r){
            cnt[a[++r]]++;
            ans=1ll*ans*(r-l+1)%MOD*inv[cnt[a[r]]]%MOD;
        }
        while(r>p->r){
            ans=1ll*ans*inv[r-l+1]%MOD*cnt[a[r]]%MOD;
            cnt[a[r--]]--;
        }
        while(l>p->l){
            cnt[a[--l]]++;
            ans=1ll*ans*(r-l+1)%MOD*inv[cnt[a[l]]]%MOD;
        }
        while(l<p->l){
            ans=1ll*ans*inv[r-l+1]%MOD*cnt[a[l]]%MOD;
            cnt[a[l++]]--;
        }
        ansq[p->pos]=ans;
    }
}
void print(){
    for(int i=1;i<=m;i++)
        printf("%d\n",ansq[i]);
}
int main()
{
    prepare();
    Read(T);
    while(T--){
        memset(cnt,0,sizeof cnt);
        init();
        solve();
        print();
    }
}
posted @ 2015-12-07 22:19  outer_form  阅读(111)  评论(0编辑  收藏  举报