hdu 5145 NPY and girls(分块+莫队+逆元)

Posted jpphy0

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hdu 5145 NPY and girls(分块+莫队+逆元)相关的知识,希望对你有一定的参考价值。

问题

hdu 5145 NPY and Girls - https://acm.hdu.edu.cn/showproblem.php?pid=5145

分析

  • 题意:编号在 [ l , r ] [l,r] [lr] 区间内的女生,有多少种访问方式?是多重集排列问题,计算式: 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;
}

以上是关于hdu 5145 NPY and girls(分块+莫队+逆元)的主要内容,如果未能解决你的问题,请参考以下文章

HDU 5145 NPY and girls(莫队算法+乘法逆元)

hdu 5145 NPY and girls 莫队

hdu NPY and girls 莫队+逆元

NPY and girls

HDU 5145 分块 莫队

HDU 1068 Girls and Boys