莫队 + 组合 + 逆元 HDU 5145

来源:互联网 发布:最短路径算法floyd实例 编辑:程序博客网 时间:2024/06/06 13:58

NPY and girls

Time Limit: 8000/4000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 1436    Accepted Submission(s): 491


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(1T10).
For each test case,there are two integers n,m(0<n,m30000) 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, a1,a2,a3,,an, which indicates the class number of each girl. (0<ai30000)
The following m lines,each line contains two integers l,r(1lrn),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
24 21 2 1 31 31 41 111 1
 

Sample Output
3121
 
题意:有N个女朋友候选人,每个人对应所在的班级,有M次询问,每次询问一个女孩区间,在这个区间里的女孩每一个人都要去看望她们,每次去一个班只能看一个女生,问访问班级有多少种情况

比如 : 三个女孩 1 1 2   询问 [1,3] 那么就有 1 1 2 , 1 2 1, 2 1 1 三种情况

思路:

我们继续根据上面的那个例子来找规律。 当有三个女生 1 1 2 的时候答案是3

那么加多一个 2 的女生的时候会是什么情况呢?   _1_1_2_ 我们可以看到,新来的女生可以在这四个位置中挑一个坐

那么原来有 3 种,现在有 4种坐法,所以就有 3 * 4 = 12 种情况了,但是我们会发现 _1_1_22 和 _1_122_ 这两种是一样的,答案应该是 6种  其实就是四个空位来分给 两个 1 和两个 2  ans2 =  C[4][2] * C[4 - 2][2]

那么原来 1 1 2 的时候是 ans1 = C[3][2] * C[3 - 2][1]  

这两者之间的关系就是 ans2 = ans1 * 4 / 2 也就是  C[3][2] * C[3 - 2][1] * (3 + 1) / (1 + 1) = C[4][2]

他的实际含义就是 有 4 个空位可以插入,但是有 2 个 2,所以有 重复的 所以应该是 * 4 / 2

那么同理 1 1 1 2 的情况由 1 1 2 得来就是 = 3 * 4 / 3 = 4

所以 对于新增加一个值x 则 flag[x]++  Ans = Ans * (r - l + 1) / (flag[x])

对于减的就相反操作    Ans = Ans * flag[x] / (r - 1 + 1)    flag[x]--

做除法的时候没法取模,所以要用逆元 inv[] 去做除法 所以 Ans / (flag[x]) = Ans * inv[flag[x]] 

需要注意的是  不能写成  dele(a[l++])  而要写成 dele(a[l]) , l++ ,否则在上传了值之后 l 就变了的话,区间长度也变了,答案就会计算错误

#include<cstdio>#include<iostream>#include<algorithm>#include<cstring>#include<cmath>using namespace std;#define maxn 30030#define mem(a,x) memset(a,x,sizeof(a))#define ll long longconst ll mod = 1000000007;struct quse{int l,r,id,pos;}q[maxn];int n,m,a[maxn],flag[maxn];int l,r;ll Ans,ans[maxn];ll inv[maxn]; int cmp(quse a,quse b){if(a.pos != b.pos)return a.pos < b.pos;return a.r < b.r;}ll Pow(ll a,ll b){ll tmp = 1;while(b){if(b & 1){tmp *= a;tmp %= mod;}a *= a;a %= mod;b /= 2;}return tmp;}void init(){for(int i = 1;i <= 30000;i++)inv[i] = Pow(i,mod - 2);}void add(int x){flag[x]++;Ans = Ans * (r - l + 1) % mod;Ans = Ans * inv[flag[x]] % mod;}void dele(int x){Ans = Ans * flag[x] % mod;Ans = Ans * inv[r - l + 1] % mod;flag[x]--;}int main(){init(); int T;scanf("%d",&T);while(T--){scanf("%d %d",&n,&m);int block = sqrt(n);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].id = i;q[i].pos = q[i].l / block;}sort(q + 1,q + 1 + m,cmp);mem(ans,0);mem(flag,0);l = 1,r = 0;Ans = 1;for(int i = 1;i <= m;i++){while(q[i].r > r){add(a[++r]);}while(q[i].r < r){dele(a[r]);r--;}while(q[i].l > l){dele(a[l]);l++;}while(q[i].l < l){add(a[--l]);}ans[q[i].id] = Ans;}for(int i = 1;i <= m;i++)printf("%lld\n",ans[i]);}return 0;}/*210 31 2 3 1 2 1 3 2 1 51 102 94 6*/


原创粉丝点击