1. 程式人生 > >【HDU 5145】NPY and girls

【HDU 5145】NPY and girls

【題目】

傳送門

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, a 1

a_1 , a 2 a_2 , a 3 a_3 ,…, a n a_n , which indicates the class number of each girl. (0< a i a_i ≤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
1 1

Sample Output

3
12
1


【分析】

題目大意:(多組資料)給出 n n 個數和 m m 個詢問,每次詢問給出兩個數 l l r r ,詢問 [ l l , r r ] 中的數有多少種不同的排列方式(例如 1 &ThickSpace; 1 &ThickSpace; 2 1\;1\;2 不同的排列方式有三種,分別為 1 &ThickSpace; 1 &ThickSpace; 2 1\;1\;2 1 &ThickSpace; 2 &ThickSpace; 1 1\;2\;1 2 &ThickSpace; 1 &ThickSpace; 1 2\;1\;1

首先有一個結論,就是對於區間 [ l l , r r ],若用 c n t ( i ) cnt(i) 表示 [ l l , r r ] 中, i i 這個數出現的次數,用 x i x_i 表示 [ l l , r r ] 中出現過的所有互不相同的數,則:
a n s = ( r l + 1 ) ! c n t ( x 1 ) ! c n t ( x 2 ) ! c n t ( x k ) ! ans=\frac{(r-l+1)!}{cnt(x_1)!*cnt(x_2)!*\cdots*cnt(x_k)!}

這個就可以理解成總共的方案數除掉相同的方案就是答案(實在不懂的話舉幾個例子就明白了)

那怎麼維護上面的式子呢,很容易想到的是用莫隊

然後注意一下預處理出階乘階乘的逆元就可以了


【程式碼】

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 30005
#define Mod 1000000007
using namespace std;
int a[N],ans[N],cnt[N],fac[N],inv[N];
struct node
{
	int l,r,bl,id;
}q[N];
bool comp(const node &p,const node &q)
{
	if(p.bl!=q.bl)
	  return p.bl<q.bl;
	return p.r<q.r;
}
int Power(int a,int b)
{
	int ans=1;
	while(b)
	{
		if(b&1)
		  ans=1ll*ans*a%Mod;
		a=1ll*a*a%Mod;
		b>>=1;
	}
	return ans;
}
void prework()
{
	int i;
	fac[0]=fac[1]=1;
	for(i=2;i<N;++i)  fac[i]=1ll*fac[i-1]*i%Mod;
	inv[N-1]=Power(fac[N-1],Mod-2);
	for(i=N-2;i>=0;--i)  inv[i]=1ll*(i+1)*inv[i+1]%Mod;
}
void Add(int &now,int id)
{
	now=1ll*now*fac[cnt[a[id]]]%Mod;
	now=1ll*now*inv[++cnt[a[id]]]%Mod;
}
void Del(int &now,int id)
{
	now=1ll*now*fac[cnt[a[id]]]%Mod;
	now=1ll*now*inv[--cnt[a[id]]]%Mod;
}
int main()
{
	int n,m,i,T;
	prework();
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&n,&m);
		int S=sqrt(n);
		for(i=1;i<=n;++i)
		  scanf("%d",&a[i]);
		for(i=1;i<=m;++i)
		{
			scanf("%d%d",&q[i].l,&q[i].r);
			q[i].id=i,q[i].bl=(q[i].l-1)/S+1;
		}
		sort(q+1,q+m+1,comp);
		int L=1,R=0,now=1;
		memset(cnt,0,sizeof(cnt));
		for(i=1;i<=m;++i)
		{
			while(R<q[i].r)  Add(now,++R);
			while(R>q[i].r)  Del(now,R--);
			while(L<q[i].l)  Del(now,L++);
			while(L>q[i].l)  Add(now,--L);
			ans[q[i].id]=1ll*fac[q[i].r-q[i].l+1]*now%Mod;
		}
		for(i=1;i<=m;++i)
		  printf("%d\n",ans[i]);
	}
	return 0;
}