LOJ#2567. 「APIO2016」划艇 离散化+DP
运用到了 NOI2019 机器人那道题的技巧.
考虑对区间进行离散化,然后设当前枚举到的两个端点为 $[l,r]$.
先处理 $[l,r)$,会遇到 $\sum_{i=1}^{n} \binom{len}{i} \binom{k}{i}$ 这个式子.
这个式子可以直接 $O(1)$ 组合数算,但是我比较懒用 $O(n^2)$ 递推算的(反正不影响总复杂度)
处理完 $[l,r)$ 后再处理 $[r,r]$ 以保证所有边界情况都被考虑好.
code:
#include <cstdio>
#include <cstring>
#include <algorithm>
#define N 704
#define ll long long
#define mod 1000000007
#define setIO(s) freopen(s".in","r",stdin)
using namespace std;
int n;
int inv[N],iv[N],fac[N],g[N],A[N];
int a[N],b[N],e[N<<1],dp[N][2];
void init() {
inv[1]=1,fac[0]=1;
for(int i=1;i<N;++i) fac[i]=(ll)fac[i-1]*i%mod;
for(int i=2;i<N;++i) iv[i]=inv[i]=(ll)(mod-mod/i)*inv[mod%i]%mod;
iv[0]=iv[1]=1;
inv[0]=1;
for(int i=1;i<N;++i) inv[i]=(ll)inv[i-1]*inv[i]%mod;
}
int C(int x,int y) {
return (ll)fac[x]*inv[y]%mod*inv[x-y]%mod;
}
int ADD(int x,int y) {
return (x+y)>=mod?x+y-mod:x+y;
}
int main() {
// setIO("input");
init();
int x,y,z,tot=0;
scanf("%d",&n);
for(int i=1;i<=n;++i) {
scanf("%d%d",&a[i],&b[i]);
e[++tot]=a[i],e[++tot]=b[i]+1;
}
sort(e+1,e+1+tot);
dp[0][0]=1;
for(int i=1;i<=tot;++i) {
if(e[i]!=e[i-1]) {
int len=e[i]-e[i-1]-1;
if(len) {
A[0]=1;
for(int j=1;j<=min(n,len);++j) {
A[j]=(ll)A[j-1]*iv[j]%mod*(len-j+1)%mod;
}
for(int j=1;j<=n;++j)
{
g[j]=0;
for(int t=1;t<=min(len,j);++t)
g[j]=ADD(g[j],(ll)A[t]*C(j-1,t-1)%mod);
}
for(int j=1;j<=n;++j) {
dp[j][1]=0;
if(a[j]<=e[i-1]&&b[j]>=e[i-1]) {
int cnt=0;
for(int k=j;k>=1;--k) {
if(a[k]<=e[i-1]&&b[k]>=e[i-1]) {
++cnt;
}
if(cnt) dp[j][1]=ADD(dp[j][1],(ll)g[cnt]*dp[k-1][0]%mod);
}
}
}
for(int j=1;j<=n;++j) {
dp[j][0]=ADD(dp[j][0],dp[j][1]);
dp[j][1]=0;
}
}
for(int j=1;j<=n;++j) {
if(a[j]<=e[i]&&b[j]>=e[i]) {
for(int k=0;k<j;++k) dp[j][1]=ADD(dp[j][1],dp[k][0]);
}
}
for(int j=1;j<=n;++j) {
dp[j][0]=ADD(dp[j][0],dp[j][1]);
dp[j][1]=0;
}
}
}
int ans=0;
for(int i=1;i<=n;++i) {
ans=ADD(ans,dp[i][0]);
}
printf("%d\n",ans);
return 0;
}

浙公网安备 33010602011771号