【HDU 5145】 NPY and girls(组合+莫队)
2 4 2 1 2 1 3 1 3 1 4 1 1 1 1 1
3 12 1
题目大意:T组输入
每组两个数 n m分别表示人数n和询问次数m
之后n个数 表示n个女孩所在教室
对于m次询问 每次一个[L,R](1 <= L <= R <= n) 问为了访问到每个女孩 访问教室的方案有几种(会出现几个女孩在一个教室的情况 但每次访问教室只能找一个女孩 同一个编号的教室是相同的)
对于单组询问[L,R] 假设有n中班级 每个班级有c个女生 总共m个女生 那么答案就是C(m,c1)*C(m-c1,c2)*C(m-c1-c2,c3)*....*C(cn,cn)
但如果每次都暴力的进行统计然后求组合数 果果的会超时
这样就要用莫队 把区间n进行分块 分成n/sqrt(n)份 进行分块排序
然后按分好的顺序进行区间的扩大或缩小 这样可以发现对组合数的求解也可以在区间转换的过程中进行
因为C(m+1,n+1) = C(m,n)*(m+1/n+1)
同样的 C(m,n) = C(m+1,n+1)*(n+1/m+1) (对于缩小的过程而言)
这样 由于n和m的范围都在30000之内 而且要求最后对一个大素数取余 可以用费马小定理快速求出范围内所有数的逆元
然后就像上面说的对区间进行转换 然后离线处理存储答案 最后输出即可 记得答案要存在排序前的位置中。。。因为这个WA找了好久错误=.=
代码如下:
#include <iostream> #include <cmath> #include <vector> #include <cstdlib> #include <cstdio> #include <cstring> #include <queue> #include <stack> #include <list> #include <algorithm> #include <map> #include <set> #define LL long long #define Pr pair<int,int> #define fread() freopen("in.in","r",stdin) #define fwrite() freopen("out.out","w",stdout) using namespace std; const int INF = 0x3f3f3f3f; const int msz = 10000; const LL mod = 1e9+7; const double eps = 1e-8; LL pow_m(LL a,LL b) { LL ans = 1; while(b) { if(b&1) ans = (ans*a)%mod; b >>= 1; a = (a*a)%mod; } return ans; } LL Inv(LL a) { return pow_m(a,mod-2); } int per; struct Range { int l,r,id; bool operator < (const struct Range a)const { return l/per == a.l/per? r < a.r: l/per < a.l/per; } }; Range rg[30030]; LL ans[30030]; LL inv[30030]; int cla[30030]; int cnt[30030]; int main() { //fread(); //fwrite(); for(int i = 1; i <= 30000; ++i) inv[i] = Inv(i); int t,n,q; scanf("%d",&t); while(t--) { scanf("%d%d",&n,&q); per = sqrt(n*1.0); for(int i = 1; i <= n; ++i) scanf("%d",&cla[i]); for(int i = 0; i < q; ++i) { scanf("%d%d",&rg[i].l,&rg[i].r); rg[i].id = i; } sort(rg,rg+q); int l = 1, r = 0; LL tmp = 1; memset(cnt,0,sizeof(cnt)); for(int i = 0; i < q; ++i) { while(r < rg[i].r) { ++r; cnt[cla[r]]++; tmp = tmp*(r-l+1)%mod*inv[cnt[cla[r]]]%mod; } while(l > rg[i].l) { --l; cnt[cla[l]]++; tmp = tmp*(r-l+1)%mod*inv[cnt[cla[l]]]%mod; } while(r > rg[i].r) { tmp = tmp*cnt[cla[r]]%mod*inv[r-l+1]%mod; cnt[cla[r]]--; --r; } while(l < rg[i].l) { tmp = tmp*cnt[cla[l]]%mod*inv[r-l+1]%mod; cnt[cla[l]]--; ++l; } ans[rg[i].id] = tmp; } for(int i = 0; i < q; ++i) printf("%I64d\n",ans[i]); } return 0; }