hdu_5145_NPY and girls(莫队算法+组合)
2016-07-05 15:14
573 查看
题目连接: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)。然后状态转移的方程就出来了,最后再预处理一下逆元,就可以开始出答案了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=3e4+7,mod=1e9+7;
//逆元筛
long long inv
={0,1};
void init(){F(i,2,N-1)inv[i]=inv[mod%i]*(mod-mod/i)%mod;}
int t,n,m,a
,sqr,cnt
,ans
;//sqr为分块的大小,cnt记录的对应教室的女孩数
//以左边边界所在的块为首关键字,左边边界所在的块为第二关键字排序
struct qy{
int l,r,lid,idx;
bool operator<(const qy& b)const{
if(lid==b.lid)return r<b.r;
else return lid<b.lid;
}
}q
;
int main(){
init(),scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m),sqr=(int)sqrt(1.0*n);
F(i,1,n)scanf("%d",a+i);
F(i,0,N-1)cnt[i]=0;
F(i,1,m){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].lid=q[i].l/sqr,q[i].idx=i;
}
sort(q+1,q+1+m);
int l=1,r=0,len=0;
long long ret=1;
F(i,1,m){
while(r<q[i].r){
r++,len++,cnt[a[r]]++;
ret=(ret*len%mod)*inv[cnt[a[r]]]%mod;
}
while(r>q[i].r){
ret=(ret*cnt[a[r]]%mod)*inv[len]%mod;
cnt[a[r]]--,r--,len--;
}
while(l>q[i].l){
l--,len++,cnt[a[l]]++;
ret=(ret*len%mod)*inv[cnt[a[l]]]%mod;
}
while(l<q[i].l){
ret=(ret*cnt[a[l]]%mod)*inv[len]%mod;
cnt[a[l]]--,l++,len--;
}
ans[q[i].idx]=ret;
}
F(i,1,m)printf("%d\n",ans[i]);
}
return 0;
}
题意:给你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)。然后状态转移的方程就出来了,最后再预处理一下逆元,就可以开始出答案了。
#include<cstdio>
#include<algorithm>
#include<cmath>
#define F(i,a,b) for(int i=a;i<=b;i++)
using namespace std;
const int N=3e4+7,mod=1e9+7;
//逆元筛
long long inv
={0,1};
void init(){F(i,2,N-1)inv[i]=inv[mod%i]*(mod-mod/i)%mod;}
int t,n,m,a
,sqr,cnt
,ans
;//sqr为分块的大小,cnt记录的对应教室的女孩数
//以左边边界所在的块为首关键字,左边边界所在的块为第二关键字排序
struct qy{
int l,r,lid,idx;
bool operator<(const qy& b)const{
if(lid==b.lid)return r<b.r;
else return lid<b.lid;
}
}q
;
int main(){
init(),scanf("%d",&t);
while(t--){
scanf("%d%d",&n,&m),sqr=(int)sqrt(1.0*n);
F(i,1,n)scanf("%d",a+i);
F(i,0,N-1)cnt[i]=0;
F(i,1,m){
scanf("%d%d",&q[i].l,&q[i].r);
q[i].lid=q[i].l/sqr,q[i].idx=i;
}
sort(q+1,q+1+m);
int l=1,r=0,len=0;
long long ret=1;
F(i,1,m){
while(r<q[i].r){
r++,len++,cnt[a[r]]++;
ret=(ret*len%mod)*inv[cnt[a[r]]]%mod;
}
while(r>q[i].r){
ret=(ret*cnt[a[r]]%mod)*inv[len]%mod;
cnt[a[r]]--,r--,len--;
}
while(l>q[i].l){
l--,len++,cnt[a[l]]++;
ret=(ret*len%mod)*inv[cnt[a[l]]]%mod;
}
while(l<q[i].l){
ret=(ret*cnt[a[l]]%mod)*inv[len]%mod;
cnt[a[l]]--,l++,len--;
}
ans[q[i].idx]=ret;
}
F(i,1,m)printf("%d\n",ans[i]);
}
return 0;
}
相关文章推荐
- 【HDU 5366】The mook jong 详解
- 【HDU 2136】Largest prime factor 详细图解
- 【HDU 1568】Fibonacci 数学公式 详解
- HDU 1568
- HDU1290
- HDU1568(Fobonacci公式)
- HDU ACM Step 2.2.2 Joseph(约瑟夫环问题)
- HDU 1405
- HDU 1297
- hdu 1205
- hdu 2087
- hdu 1016
- HDU 4898 The Revenge of the Princess’ Knight ( 2014 Multi-University Training Contest 4 )
- HDU 5592 ZYB's Premutation 线段树(查找动态区间第K大)
- HDU 5240 Exam (好水的题)
- HDU5237 Base64 大模拟
- HDU 1000
- HDU 1001
- HDU 1016 Prime Ring Problem
- HDU 1017 A Mathematical Curiosity