区间离散化+DP
AiBi太大了,一个数一个数地做肯定不行。于是先按照区间端点把区间离散化,至多形成2n−1个区间。
f[i][j][k]表示做到第i个学校,最后一个数在离散区间j中,且第j个区间内有k个学校的方案数。
f[i][j][1]可以由f[i-1][j][1]和所有f[i−n][j−m][k]转移过来,于是DP的时候记下前缀和来统计。
f[i][j][k](k≠1)+=f[i−1][j][k−1]∗(区间长度−k+1)/k
离散化的时候注意前一个区间的右端点和后一个区间的左端点不要重复,否则会有一颗赛艇的事情发生,我的方法是右端点减一。
第一维并没有什么用,把它压掉就好了- -
这题真excting,各种优化常数之后,跑了64秒过- -
#include<cstdio>
#include<algorithm>
#define N 505
#define MOD 1000000007
#define reg register
using namespace std;
int in()
{
reg char c=getchar();
reg int r=0;
while(c<'0'||c>'9')c=getchar();
while(c>='0'&&c<='9')r=r*10+c-'0',c=getchar();
return r;
}
struct Sec{int l, r, len;}s[N<<1];int scnt=0, m=0;
int n, A[N], B[N], f[2*N][N], sum[2*N], inv[600], p[N<<1];
inline int query_l(int x)
{
reg int l=1,r=scnt,ans;
for (;l<=r;)
{
int mid=(l+r)>>1;
if (x<=s[mid].l) ans=mid,r=mid-1; else l=mid+1;
}
return ans;
}
inline int query_r(int x)
{
reg int l=1,r=scnt,ans;
for (;l<=r;)
{
int mid=(l+r)>>1;
if (s[mid].r<x) ans=mid,l=mid+1; else r=mid-1;
}
return ans;
}
void init()
{
sort(p+1,p+2*n+1);
for(reg int i = 1, ii=2*n; i < ii; i++)
if(p[i]!=p[i+1])
s[++scnt]=(Sec){p[i],p[i+1]-1, p[i+1]-p[i]};
for(reg int i = 1; i <= n; i++)
{
A[i]=query_l(A[i]);
B[i]=query_r(B[i]);
}
inv[1]=1;
for(reg int i = 2; i < N; i++)
inv[i]=1ll*(MOD-MOD/i)*inv[MOD%i]%MOD;
}
void dp()
{
for(reg int i = 0; i <= scnt; i++)
sum[i]=1;
for(reg int i = 1; i <= n; i++)
{
for(reg int j = A[i]; j <= B[i]; j++)
{
for(reg int k = min(i,s[j].len); k >= 2; k--)
f[j][k]=(f[j][k]+((long long)f[j][k-1]*(s[j].len-k+1)%MOD)*inv[k])%MOD;
f[j][1]=(f[j][1]+(long long)sum[j-1]*s[j].len%MOD)%MOD;
}
sum[0]=1;
for(reg int j = 1; j <= scnt; j++)
{
sum[j]=sum[j-1];
for(reg int k = 1; k <= min(i,s[j].len); k++)
sum[j]=(sum[j]+f[j][k])%MOD;
}
}
}
int main()
{
n = in();
for(int i = 1; i <= n; i++)
{
p[i*2-1]=A[i]=in();
p[i*2]=B[i]=in()+1;
}
init(); dp();
int ans=0;
for(reg int i = 1; i <= scnt; i++)
for(reg int j = 1; j <= n; j++)
ans=(ans+f[i][j])%MOD;
printf("%d\n",ans);
return 0;
}