正题
题目链接:https://www.luogu.com.cn/problem/P3643
题目大意
求有多少个nnn个数的序列xxx满足,xi∈{0}∪[ai,bi]x_i\in \{0\}\cup[a_i,b_i]xi∈{0}∪[ai,bi]且非000数递增。
解题思路
会发现ai,bia_i,b_iai,bi很大不能太暴力的将第二维的dpdpdp设为上一个选了的数是多少。
可以考虑离散化,会将整个数轴分成最多2n−12n-12n−1个区间,但是这样我们就不能确定上个数字具体在哪里了。nnn比较小,所以我们可以考虑一种比较合理的方法就先确定这个区间中有多少个数然后再用组合数算出这个区间的方案。
设fi,jf_{i,j}fi,j表示到第iii个数(并且这个数不是000)在第jjj个区间时的方案,若从fk,l(k<i,l<j)f_{k,l}(k<i,l<j)fk,l(k<i,l<j)转移过来,也就是在[k+1,i][k+1,i][k+1,i]的数要么在jjj这个区间内,要么是000。若区间jjj的长度为lenlenlen,[k+1,i][k+1,i][k+1,i]中的数能选到区间jjj的数的个数为ccc(当然iii一定要能选到),此时的方案数应该是(len+cc)\binom{len+c}{c}(clen+c)。
可以理解为我们要在[0,len][0,len][0,len]这个范围内选出ccc个数使得非000数递增,那么我们让前面是1∼len1\sim len1∼len,然后在最后面放ccc个000,此时选到000的位置就表示这个位置是000,否则就按选择的非零数填到后面没有选择的000的位置。
那么现在就有转移
fi,j=∑k=0i−1(len+cc)∑l=0j−1fk,lf_{i,j}=\sum_{k=0}^{i-1}\binom{len+c}{c}\sum_{l=0}^{j-1}f_{k,l}fi,j=k=0∑i−1(clen+c)l=0∑j−1fk,l
后面那个前缀和优化一下时间复杂度就能到O(n3)O(n^3)O(n3)了,写起来细节有一点多。
code
#include<cstdio>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const ll N=1010,P=1e9+7;
ll n,a[N],b[N],p[N],inv[N],fac[N];
ll ans,f[N][N],g[N][N];
int main()
{// printf("%d",sizeof(f)>>20);scanf("%lld",&n);for(ll i=1;i<=n;i++){scanf("%lld%lld",&a[i],&b[i]);p[i]=a[i];b[i]++;p[i+n]=b[i];}inv[1]=1;for(ll i=2;i<=n;i++)inv[i]=P-(P/i)*inv[P%i]%P;inv[0]=1;for(ll i=1;i<=n;i++)inv[i]=inv[i-1]*inv[i]%P;sort(p+1,p+1+2*n);ll m=unique(p+1,p+1+2*n)-p-1;for(ll i=0;i<m;i++)g[0][i]=1;for(ll i=1;i<=n;i++){for(ll j=1;j<m;j++){if(p[j]>=a[i]&&p[j+1]<=b[i]){ll l=p[j+1]-p[j];fac[0]=1;for(ll k=1;k<=n;k++)fac[k]=fac[k-1]*(l+k-1)%P;for(ll k=i-1,c=1;k>=0;k--){(f[i][j]+=fac[c]*inv[c]%P*g[k][j-1]%P)%=P;if(p[j]>=a[k]&&p[j+1]<=b[k])c++;}}g[i][j]=(g[i][j-1]+f[i][j])%P;}}for(int i=1;i<=n;i++)(ans+=g[i][m-1])%=P;printf("%lld\n",ans);return 0;
}