问题
hdu 5145 NPY and Girls - https://acm.hdu.edu.cn/showproblem.php?pid=5145
分析
- 题意:编号在 [ l , r ] [l,r] [l,r] 区间内的女生,有多少种访问方式?是多重集排列问题,计算式: N ! n 1 ! ⋯ n k ! \frac{N!}{n_1!\cdots n_k!} n1!⋯nk!N!
- 方法:分块
- 莫队算法
- 查询区间排序规则
- 第一关键字:左边界所在的块(升序)
- 第二关键字:右边界升序(块号偶数)或 降序(块号奇数)
- 代码
- 查询区间排序规则
sort(q+1, q+m+1, [](query x, query y){
return x.L==y.L?((x.L&1)?x.r>y.r:x.r<y.r):x.L<y.L;
});
- 双指针初始化:初始时刻均指向第一个查询的左边界,计数器值为1;
int pl = q[1].l, pr = q[1].l;
num[a[q[1].l]] = 1, ret = 1;
- 指针移动方式
- 区间扩大优先(左指针优先左移,右指针优先右移),使得两指针不会交叉
- 区间扩大时(先更新指针,然后加入新指元素)
- 区间减小时(先去除当前所指元素,然后更新指针值)
while(pl > q[i].l) add(--pl);
while(pr < q[i].r) add(++pr);
while(pl < q[i].l) sub(pl++);
while(pr > q[i].r) sub(pr--);
- 逆元:暴力枚举
代码
/* hdu 5145 NPY and girls */
#include<bits/stdc++.h>
using namespace std;
#define MXN 30010
#define MXB 175 // 分块数量
#define siz MXB // 分块大小
#define M 1000000007
int n, m, a[MXN], ans[MXN], num[MXN], ret, inv[MXN], finv[MXN]={1};
struct query{int l, r, L, R, n;} q[MXN];
void add(int p){
ret = 1LL*ret*inv[++num[a[p]]]%M;
}
void sub(int p){
ret = 1LL*ret*(num[a[p]]--)%M;
};
void ex_gcd(int aa, int bb, int &x, int &y){
if(bb == 0) {
x = 1, y = 0;
return;
}
ex_gcd(bb, aa%bb, x, y);
int tmp = x;
x = y;
y = tmp-aa/bb*y;
}
void solve(){
scanf("%d%d", &n, &m);
for(int i = 1; i <= n; ++i) scanf("%d", a+i);
for(int i = 1; i <= m; ++i){
scanf("%d%d", &q[i].l, &q[i].r), q[i].n = i;
q[i].L = q[i].l/siz, q[i].R = q[i].r/siz;
}
sort(q+1, q+m+1, [](query x, query y){
return x.L==y.L?((x.L&1)?x.r>y.r:x.r<y.r):x.L<y.L;
});
memset(num, 0, sizeof num);
memset(ans, 0, sizeof ans);
int pl = q[1].l, pr = q[1].l;
num[a[q[1].l]] = 1, ret = 1;
for(int i = 1; i <= m; ++i){
while(pl > q[i].l) add(--pl);
while(pr < q[i].r) add(++pr);
while(pl < q[i].l) sub(pl++);
while(pr > q[i].r) sub(pr--);
ans[q[i].n] = 1LL*ret*finv[q[i].r-q[i].l+1]%M;
}
for(int i = 1; i <= m; ++i) printf("%d\n", ans[i]);
}
int main(){
int t;
for(int i = 1; i < MXN-1; ++i)
ex_gcd(i, M, inv[i], inv[i+1]), inv[i] = (inv[i]%M+M)%M;
for(int i = 1; i < MXN; ++i) finv[i] = 1LL*finv[i-1]*i%M;
scanf("%d", &t);
while(t--) solve();
return 0;
}