【bzoj4584】[Apio2016]赛艇 dp

首先,对区间离散化,最终会最多形成2n-1个区间
f[i][j][k]表示考虑到第i个学校,最后一个数落在j区间中,第j个区间中有k个数的方案数
sum[t]=∑f[i-1][j][p] (1<=j<t)
f[i][j][k]=f[i-1][j][k]+f[i-1][j][k-1]*(len[j]-k+1)/k
f[i][j][1]=f[i-1][j][1]+sum[j-1]*len[j]
前缀和优化一下就好了,考试的时候没有想清楚落在同一个区间中如何处理,所以导致最终思路偏了

可以优化空间,倒序枚举k即可

我该如何骂我自己好呢?


#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
#include<iostream>
#include<algorithm>
#define maxn 510
#define mod 1000000007

using namespace std;

struct yts
{
	int l,r;
}q[maxn],seq[2*maxn];

int a[2*maxn];
int inv[1010];
int f[2*maxn][maxn],len[2*maxn];
int sum[2*maxn];
int dp[maxn][2*maxn];
int n,m,tot,num;

int query_l(int x)
{
	int l=1,r=num,ans;
	while (l<=r)
	{
		int mid=(l+r)/2;
		if (x<=seq[mid].l) ans=mid,r=mid-1; else l=mid+1;
	}
	return ans;
}

int query_r(int x)
{
	int l=1,r=num,ans;
	while (l<=r)
	{
		int mid=(l+r)/2;
		if (seq[mid].r<x) ans=mid,l=mid+1; else r=mid-1;
	}
	return ans;
}

int power(int x,int y)
{
	int ans=1;
	while (y)
	{
		if (y&1) ans=(long long)ans*x%mod;
		x=(long long)x*x%mod;
		y>>=1;
	}
	return ans;
}

int main()
{
	scanf("%d",&n);
	for (int i=1;i<=n;i++)
	{
		int x,y;
		scanf("%d%d",&x,&y);
		y++;
		q[i].l=x;q[i].r=y;
		a[++tot]=x;a[++tot]=y;
	}
	for (int i=1;i<=1000;i++) inv[i]=power(i,mod-2);
	sort(a+1,a+tot+1);
	for (int i=1;i<tot;i++) 
	  if (a[i]!=a[i+1]) seq[++num].l=a[i],seq[num].r=a[i+1]-1,len[num]=a[i+1]-a[i];
	for (int i=1;i<=n;i++) q[i].l=query_l(q[i].l),q[i].r=query_r(q[i].r);
	for (int i=0;i<=num;i++) sum[i]=1;
	for (int i=1;i<=n;i++)
	{
		for (int j=1;j<=num;j++) dp[i][j]=dp[i-1][j];
		for (int j=q[i].l;j<=q[i].r;j++) dp[i][j]++;
	}
	for (int i=1;i<=n;i++)
	{
	    for (int j=q[i].l;j<=q[i].r;j++)
	    {
	      	for (int k=dp[i][j];k>=2;k--)
	        	f[j][k]=(f[j][k]+((long long)f[j][k-1]*(len[j]-k+1)%mod)*inv[k]%mod)%mod;
	        f[j][1]=(f[j][1]+(long long)sum[j-1]*len[j]%mod)%mod;
	    }
	    sum[0]=1;
	    for (int j=1;j<=num;j++)
	    {
	    	sum[j]=sum[j-1];
	        for (int k=1;k<=dp[i][j];k++) sum[j]=(sum[j]+f[j][k])%mod;
	    }
	}
	int ans=0;
	for (int i=1;i<=num;i++)
	  for (int j=1;j<=n;j++)
	    ans=(ans+f[i][j])%mod;
	printf("%d\n",ans);
	return 0;
}


你可能感兴趣的:(【bzoj4584】[Apio2016]赛艇 dp)