【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 a1, a 2 a_2 a2, a 3 a_3 a3,…, a n a_n an, which indicates the class number of each girl. (0< a i a_i ai≤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 n 个数和 m m m 个询问,每次询问给出两个数 l l l r r r,询问 [ l l l , r r r ] 中的数有多少种不同的排列方式(例如 1 &ThickSpace; 1 &ThickSpace; 2 1\;1\;2 112 不同的排列方式有三种,分别为 1 &ThickSpace; 1 &ThickSpace; 2 1\;1\;2 112 1 &ThickSpace; 2 &ThickSpace; 1 1\;2\;1 121 2 &ThickSpace; 1 &ThickSpace; 1 2\;1\;1 211

首先有一个结论,就是对于区间 [ l l l , r r r ],若用 c n t ( i ) cnt(i) cnt(i) 表示 [ l l l , r r r ] 中, i i i 这个数出现的次数,用 x i x_i xi 表示 [ l l l , r 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)!} ans=cnt(x1)!cnt(x2)!cnt(xk)!(rl+1)!

这个就可以理解成总共的方案数除掉相同的方案就是答案(实在不懂的话举几个例子就明白了)

那怎么维护上面的式子呢,很容易想到的是用莫队

然后注意一下预处理出阶乘阶乘的逆元就可以了


【代码】

#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;
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值