【BZOJ4584】[Apio2016]赛艇 DP

【BZOJ4584】[Apio2016]赛艇

Description

在首尔城中,汉江横贯东西。在汉江的北岸,从西向东星星点点地分布着个划艇学校,编号依次为到。每个学校都拥有若干艘划艇。同一所学校的所有划艇颜色相同,不同的学校的划艇颜色互不相同。颜色相同的划艇被认为是一样的。每个学校可以选择派出一些划艇参加节日的庆典,也可以选择不派出任何划艇参加。如果编号为的学校选择派出划艇参加庆典,那么,派出的划艇数量可以在Ai至Bi之间任意选择(Ai<=Bi)。值得注意的是,编号为i的学校如果选择派出划艇参加庆典,那么它派出的划艇数量必须大于任意一所编号小于它的学校派出的划艇数量。输入所有学校的Ai、Bi的值,求出参加庆典的划艇有多少种可能的情况,必须有至少一艘划艇参加庆典。两种情况不同当且仅当有参加庆典的某种颜色的划艇数量不同

Input

 第一行包括一个整数N,表示学校的数量。接下来N行,每行包括两个正整数,用来描述一所学校。其中第行包括的两个正整数分别表示Ai,Bi(1<=Ai<=Bi<=10^9),N<=500

Output

 输出一行,一个整数,表示所有可能的派出划艇的方案数除以1,000,000,007得到的余数

Sample Input

2
1 2
2 3

Sample Output

7

题解:一开始设的是二维状态,显然不可做啊。。。

先离散化得到2*n个区间,然后用f[i][j][k]表示前i个学校,最后一个学校的数量在第j个区间中,并且第j个区间中已经有k个数的方案数。

如果上一个学校与当前学校不再同一个区间,那么我们维护s[j]表示$sumlimits_{l<j}f[i][l][..]$,那么直接用s更新f即可。如果上一个学校与当前学校在同一个区间,那么这个区间中的方案数显然是一个组合数,维护一下组合数就好了。

如果采用01背包式的枚举顺序,在空间上可以省掉一维。

 

#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
typedef long long ll;
const ll P=1000000007;
int n,m;
ll ans;
int A[510],B[510],siz[1010];
ll ref[1010],f[1010][510],s[1010],ine[510];
struct node
{
	int val,org;
}num[1010];
bool cmp(node a,node b)
{
	return a.val<b.val;
}
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<'0'||gc>'9')	{if(gc=='-')	f=-f;	gc=getchar();}
	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
	return ret*f;
}
int main()
{
	n=rd();
	int i,j,k;
	for(i=1;i<=n;i++)	num[i].val=rd()-1,num[i].org=i,num[i+n].val=rd(),num[i+n].org=i;
	sort(num+1,num+2*n+1,cmp);
	for(i=1;i<=2*n;i++)
	{
		if(i==1||num[i].val>num[i-1].val)	ref[++m]=num[i].val;
		if(!A[num[i].org])	A[num[i].org]=m+1;
		else	B[num[i].org]=m;
	}
	ine[1]=1;
	for(i=2;i<=n;i++)	ine[i]=P-(P/i)*ine[P%i]%P;
	for(i=1;i<=n;i++)
	{
		for(s[0]=1,j=1;j<=B[i];j++)
		{
			s[j]=s[j-1];
			for(k=1;k<=siz[j];k++)	s[j]=(s[j]+f[j][k])%P;
		}
		for(j=B[i];j>=A[i];j--)
		{
			siz[j]=min(ref[j]-ref[j-1],siz[j]+1ll);
			for(k=siz[j];k>=2;k--)	f[j][k]=(f[j][k]+f[j][k-1]*(ref[j]-ref[j-1]-k+1)%P*ine[k])%P;
			f[j][1]=(f[j][1]+s[j-1]*(ref[j]-ref[j-1]))%P;
		}
	}
	for(i=1;i<=m;i++)	for(j=1;j<=siz[i];j++)	ans=(ans+f[i][j])%P;
	printf("%lld",ans);
	return 0;
}

 

原文地址:https://www.cnblogs.com/CQzhangyu/p/7670345.html