BZOJ 4584 【APIO2016】 賽艇

題目連接:賽艇php

  講道理好好的Boat爲啥要翻譯成賽艇呢……題面中不也是划艇麼……ios

  這道題考慮一下dp。因爲划艇數量過於龐大,因此確定不能直接記錄到dp狀態中。因此一個想法就是把數量離散化,而後把每一個學校的數量在哪一段內記錄下來。也就是說\(f_{i,j,k}\)表示前\(i\)個學校,第\(i\)所學校派出的划艇數量在區間\(j\)內,而且區間\(j\)內共有\(k\)個學校的方案數。而後分類討論一下轉移:數組

  當\(k\ne 1\)時,有:優化

\begin{aligned}
f_{i,j,k} &=\frac{C(len_j,k)}{C(len_j,k-1)}\sum_{i'=0}^{i-1}f_{i',j,k-1}  \\
&=\frac{len_j-k+1}{k}\sum_{i'=0}^{i-1}f_{i',j,k-1}
\end{aligned}spa

  當\(k=1\)時,有:翻譯

\begin{aligned}
f_{i,j,k}=\sum_{i'=0}^{i-1}\sum_{j'=0}^{j-1}\sum_{k'=0}^{i'}f_{i',j',k'}
\end{aligned}blog

  因而前綴和優化便可。能夠發現記錄兩個前綴和以後\(f\)數組不必記錄了。時間複雜度\(O(n^3)\),空間複雜度\(O(n^2)\)。get

  下面貼代碼(常數略大):string

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
#define N 510
#define mod 1000000007

using namespace std;
typedef long long llg;

int le[N],ri[N],n,ni[N],d[N<<1],ld;
llg s1[N][N<<1],s2[N<<1][N];

void gi(int &x){x=lower_bound(d+1,d+ld+1,x)-d;}
int mi(llg a,int b){
	llg s=1;
	while(b){
		if(b&1) s=s*a%mod;
		a=a*a%mod; b>>=1;
	}
	return s;
}

int main(){
	File("a");
	scanf("%d",&n); ni[0]=1;
	for(int i=1;i<=n;i++){
		scanf("%d %d",&le[i],&ri[i]);
		d[++ld]=le[i],d[++ld]=++ri[i];
		ni[i]=mi(i,mod-2);
	}
	sort(d+1,d+ld+1); ld=unique(d+1,d+ld+1)-d-1;
	for(int i=1;i<=n;i++) gi(le[i]),gi(ri[i]);
	for(int i=0;i<=ld;i++) s1[0][i]=1;
	for(int i=1,x,y;i<=n;i++){
		for(int j=ri[i]-1;x=d[j+1]-d[j],j>=le[i];j--){
			for(int k=min(x,i);k>1;k--){
				y=s2[j][k-1]*ni[k]%mod*(x-k+1)%mod;
				(s1[i][j]+=y)%=mod,(s2[j][k]+=y)%=mod;
			}
			y=s1[i-1][j-1]*x%mod;
			(s1[i][j]+=y)%=mod,(s2[j][1]+=y)%=mod;
		}
		s1[i][0]=1;
		for(int j=1;j<=ld;j++){
			s1[i][j]+=s1[i-1][j]+s1[i][j-1];
			(s1[i][j]-=s1[i-1][j-1])%=mod;
			if(s1[i][j]<0) s1[i][j]+=mod;
		}
	}
	printf("%lld",(s1[n][ld]+mod-1)%mod);
	return 0;
}
相關文章
相關標籤/搜索