CF1295F Good Contest / [APIO2016] 划艇
先离散化,设 \(f_i\) 为考虑前 \(i\) 个元素的方案数,枚举第 \(i\) 个元素处在第 \(j\) 个区间,同时枚举一起在第 \(j\) 个区间的元素个数,用组合数计算方案数,\(DP\) 过程中处理组合数就是 \(O(n^3)\) 了。
第一题要算 \(n\) 个元素放到值域为 \(m\) 的区间中形成不降序列的方案数,第二题要算 \(n\) 个元素放到值域为 \(m\) 的区间中形成严格递增序列的方案数,且每个元素可以不选,但最后一个元素必须选,第一题的不降序列其实就是随便放,用隔板法即可得到 \(\binom{n+m-1}{m-1}\),转化一下第二题的模型就能发现其方案数和第一题一样,也可以推导证明:
\[\large \sum_{i=1}^n \binom{n-1}{i-1}\binom{m}{i}=\sum_{i=0}^{n-1} \binom{n-1}{i}\binom{m}{m-i-1}=\binom{n+m-1}{m-1}
\]
第一题:
#include<bits/stdc++.h>
#define maxn 110
#define p 998244353
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,tot;
ll val=1;
int l[maxn],r[maxn],s[maxn];
ll f[maxn],g[maxn];
ll inv(ll x)
{
ll v=1,y=p-2;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
int main()
{
read(n);
for(int i=1;i<=n;++i)
{
read(l[i]),read(r[i]),r[i]++,val=val*inv(r[i]-l[i])%p;
s[++tot]=l[i],s[++tot]=r[i];
}
sort(s+1,s+tot+1),tot=unique(s+1,s+tot+1)-s-1;
for(int i=1;i<=n;++i)
l[i]=lower_bound(s+1,s+tot+1,l[i])-s,r[i]=lower_bound(s+1,s+tot+1,r[i])-s;
f[0]=1;
for(int i=tot-1;i;--i)
{
g[0]=1;
for(int j=1;j<=n;++j) g[j]=g[j-1]*(s[i+1]-s[i]+j-1)%p*inv(j)%p;
for(int j=n;j;--j)
{
if(l[j]>i||r[j]<=i) continue;
for(int k=j;k;--k)
{
if(l[k]<=i&&r[k]>i) f[j]=(f[j]+f[k-1]*g[j-k+1]%p)%p;
else break;
}
}
}
printf("%lld",f[n]*val%p);
return 0;
}
第二题:
#include<bits/stdc++.h>
#define maxn 1010
#define p 1000000007
using namespace std;
typedef long long ll;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,tot;
ll ans;
int l[maxn],r[maxn],s[maxn];
ll f[maxn],g[maxn];
ll inv(ll x)
{
ll v=1,y=p-2;
while(y)
{
if(y&1) v=v*x%p;
x=x*x%p,y>>=1;
}
return v;
}
int main()
{
read(n);
for(int i=1;i<=n;++i) read(l[i]),read(r[i]),s[++tot]=l[i],s[++tot]=++r[i];
sort(s+1,s+tot+1),tot=unique(s+1,s+tot+1)-s-1;
for(int i=1;i<=n;++i) l[i]=lower_bound(s+1,s+tot+1,l[i])-s,r[i]=lower_bound(s+1,s+tot+1,r[i])-s;
f[0]=1;
for(int i=1;i<tot;++i)
{
g[0]=1;
for(int j=1;j<=n;++j) g[j]=g[j-1]*(s[i+1]-s[i]+j-1)%p*inv(j)%p;
for(int j=n;j;--j)
if(l[j]<=i&&r[j]>i)
for(int k=j,t=0;k;--k)
t+=l[k]<=i&&r[k]>i,f[j]=(f[j]+f[k-1]*g[t]%p)%p;
}
for(int i=1;i<=n;++i) ans=(ans+f[i])%p;
printf("%lld",ans);
return 0;
}