P3643 [APIO2016] 划艇 题解
题目描述
给定 \(n\) 个区间 \([a_i,b_i]\) ,第 \(i\) 个数要么不选,要么在 \([a_i,b_i]\) 中选择。
要求选择的数严格递增,求方案数对 \(10^9+7\) 取模的结果。
数据范围
- \(1\le n\le 500,1\le a_i\le b_i\le 10^9\) 。
时间限制 \(\texttt{3s}\) ,空间限制 \(\texttt{125MB}\) 。
分析
容易想到一个简单的 \(\texttt{dp}\) : \(f_{i,j}\) 表示考虑前 \(i\) 个数,最后一个被选的数为 \(j\) 的方案数。
然而值域过大,所以这条路行不通。
将端点离散化,在一个长为 \(len\) 的区间中选 \(j\) 个严格递增的数的方案数为 \(\binom{len}j\) !
重新设计状态, \(f_{i,j,k}\) 表示考虑前 \(i\) 个数,最后一个被选的数在第 \(j\) 个区间中,这个区间已经选了 \(k\) 个数的方案数。
转移需要分类讨论:
- 不选: \(f_{i,j,k}\gets f_{i-1,j,k}\) 。
- 新开一个区间: \(\forall l\le x\lt r,f_{i,x,1}\gets\sum_{j=0}^{x-1}\sum_{k=0}^if_{i-1,j,k}\cdot\binom{len_j}k\) 。
- 不新开区间: \(\forall l\le x\lt r,f_{i,x,k}\gets f_{i-1,x,k-1}\) 。
递推预处理 \(\binom{len_j}k\) 的值,再用前缀和优化即可做到 \(\mathcal O(n^3)\) 。
#include<bits/stdc++.h>
using namespace std;
const int maxn=1005,mod=1e9+7;
int m,n,res;
int a[maxn],b[maxn],h[maxn],inv[maxn];
int c[maxn][maxn];
int f[2][maxn][505];
inline int qpow(int a,int k)
{
int res=1;
for(;k;a=1ll*a*a%mod,k>>=1) if(k&1) res=1ll*res*a%mod;
return res;
}
inline void add(int &x,int y)
{
if((x+=y)>=mod) x-=mod;
}
int main()
{
scanf("%d",&n),inv[0]=c[0][0]=1;
for(int i=1;i<=n;i++)
{
scanf("%d%d",&a[i],&b[i]);
h[++m]=a[i],h[++m]=b[i]+1,inv[i]=1ll*inv[i-1]*qpow(i,mod-2)%mod;
}
sort(h+1,h+m+1);
m=unique(h+1,h+m+1)-h-1;
for(int i=1;i<m;i++)
{
int len=h[i+1]-h[i];
for(int j=0,cur=1;j<=n;cur=cur*(len+1ll-++j)%mod) c[i][j]=1ll*cur*inv[j]%mod;
}
f[0][0][0]=1;
for(int i=1;i<=n;i++)
{
memcpy(f[i&1],f[(i-1)&1],sizeof(f[i&1]));
int l=lower_bound(h+1,h+m+1,a[i])-h;
int r=lower_bound(h+1,h+m+1,b[i]+1)-h;
for(int j=0,cur=0;j<r;j++)
{
if(j>=l)
{
add(f[i&1][j][1],cur);
for(int k=1;k<=i;k++) add(f[i&1][j][k],f[(i-1)&1][j][k-1]);
}
for(int k=0;k<=i;k++) cur=(cur+1ll*f[(i-1)&1][j][k]*c[j][k])%mod;
}
}
for(int j=1;j<m;j++) for(int k=1;k<=n;k++) res=(res+1ll*f[n&1][j][k]*c[j][k])%mod;
printf("%d\n",res);
return 0;
}
本文来自博客园,作者:peiwenjun,转载请注明原文链接:https://www.cnblogs.com/peiwenjun/p/17415725.html
浙公网安备 33010602011771号