hdu_5145_NPY and girls(莫队算法+组合)

题目连接:http://acm.hdu.edu.cn/showproblem.php?pid=5145

题意:给你n,m,共有n个女孩,标号为1—n,n个数xi表示第ith个女孩在第xi个教室,然后下面有m个询问,每个询问有l,r两个数,表示要去找编号为l到r的女孩,每进一次教室只能找一个女孩,问有多少种组合方式,不同的组合方式定义为去教室的顺序不同。

题解:这题是离线的,很容易想到用莫队分块做,主要是要找出状态转移的方程,对于询问区间[l,r],假设这个区间的女孩一共分布在k个教室,每个教室有a1,a2,a3....ak个女孩

则这个区间的ans=C(r-l+1,a1)*C(r-l+1-a1,a2)......C(r-l+1-a1-a2..-ak-1,ak),整理方程,消去约数 得ans=(r-l+1)!/a1!*a2!*a3!...ak!。然后这时候 当r=r+1时,假设r+1这个女孩在a1这个教室,那么此时的ans用上面的式子算出来得ans=(r-l+2)!/(a1+1)*a1!*a2!*a3!...ak!。我们可以发现 ans([l,r+1])=ans([l,r])*(r-l+2)/(a1+1)。然后状态转移的方程就出来了,最后再预处理一下逆元,就可以开始出答案了。


 1 #include<cstdio>
 2 #include<algorithm>
 3 #include<cmath>
 4 #define F(i,a,b) for(int i=a;i<=b;i++)
 5 using namespace std;
 6 const int N=3e4+7,mod=1e9+7;
 7 //逆元筛
 8 long long inv[N]={0,1};
 9 void init(){F(i,2,N-1)inv[i]=inv[mod%i]*(mod-mod/i)%mod;}
10 
11 int t,n,m,a[N],sqr,cnt[N],ans[N];//sqr为分块的大小,cnt记录的对应教室的女孩数
12 //以左边边界所在的块为首关键字,左边边界所在的块为第二关键字排序
13 struct qy{
14     int l,r,lid,idx;
15     bool operator<(const qy& b)const{
16         if(lid==b.lid)return r<b.r;
17         else return lid<b.lid;
18     }
19 }q[N];
20 
21 int main(){
22     init(),scanf("%d",&t);
23     while(t--){
24         scanf("%d%d",&n,&m),sqr=(int)sqrt(1.0*n);
25         F(i,1,n)scanf("%d",a+i);
26         F(i,0,N-1)cnt[i]=0;
27         F(i,1,m){
28             scanf("%d%d",&q[i].l,&q[i].r);
29             q[i].lid=q[i].l/sqr,q[i].idx=i;
30         }
31         sort(q+1,q+1+m);
32         int l=1,r=0,len=0;
33         long long ret=1;
34         F(i,1,m){
35             while(r<q[i].r){
36                 r++,len++,cnt[a[r]]++;
37                 ret=(ret*len%mod)*inv[cnt[a[r]]]%mod;
38             }
39             while(r>q[i].r){
40                 ret=(ret*cnt[a[r]]%mod)*inv[len]%mod;
41                 cnt[a[r]]--,r--,len--;
42             }
43             while(l>q[i].l){
44                 l--,len++,cnt[a[l]]++;
45                 ret=(ret*len%mod)*inv[cnt[a[l]]]%mod;
46             }
47             while(l<q[i].l){
48                 ret=(ret*cnt[a[l]]%mod)*inv[len]%mod;
49                 cnt[a[l]]--,l++,len--;
50             }
51             ans[q[i].idx]=ret;
52         }
53         F(i,1,m)printf("%d\n",ans[i]);
54     }
55     return 0;
56 }
View Code

 

转载于:https://www.cnblogs.com/bin-gege/p/5696099.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值