HDU 5145 分块 莫队

给定n个数,q个询问[l,r]区间,每次询问该区间的全排列多少种。

数值都是30000规模

首先考虑计算全排列,由于有同种元素存在,相当于每次在len=r-l+1长度的空格随意放入某种元素即$\binom{len}{k_1}$,那么下种元素即为$\binom{len-k_1}{k2}$,以此类推,直至最后直接填满,那么全排列为${\frac{len!}{k_1!k_2!…k_n!}}$

然后可以发现可以直接O(1)求得左右相邻区间的值(就是乘或除),那么考虑分块莫队。

 

/** @Date    : 2017-09-23 18:57:10
  * @FileName: HDU 5145 分块 莫队.cpp
  * @Platform: Windows
  * @Author  : Lweleth (SoungEarlf@gmail.com)
  * @Link    : https://github.com/
  * @Version : $Id$
  */
#include <bits/stdc++.h>
#define LL long long
#define PII pair<int ,int>
#define MP(x, y) make_pair((x),(y))
#define fi first
#define se second
#define PB(x) push_back((x))
#define MMG(x) memset((x), -1,sizeof(x))
#define MMF(x) memset((x),0,sizeof(x))
#define MMI(x) memset((x), INF, sizeof(x))
using namespace std;

const int INF = 0x3f3f3f3f;
const int N = 1e5+20;
const double eps = 1e-8;
const LL mod = 1e9 + 7;

int k[30010];
int a[30010];
int blc[30010];
LL fac[30010];
LL inv[30010];
LL res[30010];
struct yuu
{
	LL l, r;
	int m;
}b[30010];

int cmp(yuu a, yuu b)
{
	if(blc[a.l] != blc[b.l])
		return a.l < b.l;
	return a.r < b.r;
}

void init()
{
	fac[0] = fac[1] = 1;
	inv[0] = inv[1] = 1;
	for(LL i = 2; i <= 30005; i++)
	{
		fac[i] = (fac[i - 1] * i % mod + mod) % mod;
		inv[i] = (mod - mod / i) * inv[mod % i] % mod;
	}
}
int main()
{
	init();
	int T;
	cin >> T;
	while(T--)
	{
		LL n, q;
		scanf("%lld%lld", &n, &q);
		int sqr = sqrt(1.0 * n);
		for(int i = 1; i <= n; i++)
			scanf("%d", a + i), blc[i] = (i - 1) / sqr + 1;
		for(int i = 1; i <= q; i++)
		{
			scanf("%lld%lld", &b[i].l, &b[i].r);
			b[i].m = i;
		}
		sort(b + 1, b + 1 + q, cmp);
		MMF(k);
		LL l = 1, r = 0;
		LL ans = 1, cnt = 0;
		for(int i = 1; i <= q; i++)
		{
			while(r < b[i].r)
			{
				r++;
				k[a[r]]++;
				cnt++;
				ans = (ans * cnt % mod * inv[k[a[r]]] % mod + mod) % mod;
			}
			while(l > b[i].l)
			{
				l--;
				cnt++;
				k[a[l]]++;
				ans = (ans * cnt % mod * inv[k[a[l]]] % mod + mod) % mod;
			}
			while(r > b[i].r)
			{
				ans = (ans * inv[cnt] % mod * k[a[r]] % mod + mod) % mod;
				cnt--;
				k[a[r]]--;
				r--;
			}
			while(l < b[i].l)
			{	
				ans = (ans * inv[cnt] % mod * k[a[l]] % mod + mod) % mod;
				cnt--;
				k[a[l]]--;
				l++;
			}
			while(ans < 0)
				ans += mod;
			res[b[i].m] = ans;
		}
		for(int i = 1; i <= q; i++)
			printf("%lld\n", res[i]);
	}
    return 0;
}
posted @ 2017-09-23 22:55  Lweleth  阅读(205)  评论(0编辑  收藏  举报