Loading

P3643 [APIO2016] 划艇

题意

给你两个序列 \(a,b\),求严格递增的序列 \(c\) 的个数,满足:\(\forall i,c_i\in[a_i,b_i]\)。特别的,如果 \(c_i=0\) 则无视当前这个 \(c_i\)

Solution

好困难的 dp,耗我半个晚上。

首先是对区间离散化,然后转化成一堆区间。此时对 \(dp\) 的定义是:\(dp_{i,j}\) 表示在考虑第 \(i\) 个学校的时候,这个学校派出了在第 \(j\) 个区间内的划艇数。

然后考虑此前每个学校可能的贡献。我们枚举有多少学校也是在区间 \(j\) 内的,假设是 \(p+1\sim i\) 都在,然后枚举 \(p\) 所在的区间,假设为 \(k\)。不难发现,在 \([p+1,i]\) 内的学校,如果有 \(m\) 所能在区间 \(j\) 内,那么这个区间内的方案数是:

\[{m+L_j-1\choose m} \]

因为在 \(L_j\) 个盒子中放 \(m\) 个球,每个盒子限放一个,有球可以不放。我们可以加 \(m\) 个盒子,然后任取 \(m\) 个放入球,再拿走这 \(m\) 个盒子。由于我们当前枚举的必须放,所以要减一。

这样我们就有:

\[dp_{i,j}=\sum_{p<i}\sum_{k<j}{m+L_j-1\choose m}dp_{p,k} \]

然后考虑优化这个东西。我们还是可以枚举 \(p\),然后维护 \(m\),把 \(k\) 前缀优化掉。还是容易做到的。令:

\[pre_{i,j}=\sum_{k\le j} dp_{i,k} \]

那么转移就成了:

\[dp_{i,j}=\sum_{p<i}{m+L_j-1\choose m}pre_{p,j-1} \]

组合数就边求边推好了:

\[{m+L_j-1\choose m}={m-1+L_j-1\choose m-1}\times \dfrac{m+L_j-1}{m} \]

复杂度是 \(O(n^3)\)

Code

// Problem: 
//     P3643 [APIO2016] 划艇
//   
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P3643
// Memory Limit: 125 MB
// Time Limit: 3000 ms

#include<bits/stdc++.h>
#define ll long long
#define inf (1<<30)
#define INF (1ll<<60)
#define pb emplace_back
#define pii pair<int,int>
#define mkp make_pair
#define fi first
#define se second
#define all(a) a.begin(),a.end()
#define siz(a) (int)a.size()
#define clr(a) memset(a,0,sizeof(a))
#define rep(i,j,k) for(int i=(j);i<=(k);i++)
#define per(i,j,k) for(int i=(j);i>=(k);i--)
#define pt(a) cerr<<#a<<'='<<a<<' '
#define pts(a) cerr<<#a<<'='<<a<<'\n'
#define int long long
using namespace std;
const int MAXN=510;
const int MOD=1e9+7;
int inv[MAXN],a[MAXN],b[MAXN];
int dp[MAXN][MAXN<<1],pre[MAXN][MAXN<<1];
vector<int> lsh;
signed main()
{
	ios::sync_with_stdio(0);
	cin.tie(0);cout.tie(0);
	inv[1]=1;rep(i,2,MAXN-10) inv[i]=((MOD-MOD/i)*inv[MOD%i]%MOD)%MOD;
	int n;cin>>n;
	rep(i,1,n) cin>>a[i]>>b[i],b[i]++,lsh.pb(a[i]),lsh.pb(b[i]);
	sort(all(lsh));lsh.erase(unique(all(lsh)),lsh.end());
	
	rep(i,0,siz(lsh)-1) pre[0][i]=1;
	rep(i,1,n) rep(j,1,siz(lsh)-1){
		if(a[i]<=lsh[j-1]&&b[i]>=lsh[j]){
			int L=lsh[j]-lsh[j-1],C=L,m=1;
			per(p,i-1,0){
				(dp[i][j]+=C*pre[p][j-1]%MOD)%=MOD;
				if(a[p]<=lsh[j-1]&&b[p]>=lsh[j])
					m++,C=C*(m+L-1)%MOD*inv[m]%MOD;
			}
		}pre[i][j]=(pre[i][j-1]+dp[i][j])%MOD;
	}
	int ans=0;
	rep(i,1,n) ans=(ans+pre[i][siz(lsh)-1])%MOD;
	cout<<ans<<'\n';
	return 0;
}
posted @ 2022-11-14 13:46  ZCETHAN  阅读(23)  评论(0编辑  收藏  举报