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