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)。然後狀態轉移的方程就出來了,最後再預處理一下逆元,就可以開始出答案了。


#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[N]={0,1};
void init(){F(i,2,N-1)inv[i]=inv[mod%i]*(mod-mod/i)%mod;}

int t,n,m,a[N],sqr,cnt[N],ans[N];//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[N];

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;
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章