APIO2016 划艇
APIO2016 划艇
看到T2好像还可做,就仔细想了很长时间,想到了f[i][j]+=f[i-1][p] (p<=j-1),但是回头一瞧,1e9的数据。。。。。。。。。。。。。还玩个球啊,老老实实打暴力。但是n最小是100,显然没有暴力分。。。。。。。。。
好吧好吧,那就不属于我了,1e9也想过离散化,可是后面就没什么进展了。
DP
dp的话得先把1e9的问提解决掉,要不然没法搞,没错,肯定要离散化。可是离散化了后我怎么整呢,原本是实在的位置,现在就是一群无情的数字。额,其实这数字还是有情的唉,他能断开不同的区间,我们求每一段区间的贡献就好了。
啥?区间贡献,这不更没法搞了哈哈。仔细想一下,本段区间对i点的贡献,跟推出来的那个式子好像很有关系的,我们只需要知道i前面有几个点也在区间j内,比如说现在到了k点,他可以在区间j内,因为k到i之间的点是可以取0的,所以不用管他,我们只关心在k和i之间可以有几个点出现在区间j内就好,那么这里每一个k的贡献就是k之前的点在j-1之前区间的方案数×k到i的m个点在j内的方案数,前者好说,我们递推过来就求过了,关键是看后者。
对于长度为len的区间,任取m个位置ans就是C(len,m),但是现在问题是我不一定m个点都取位置,他可以不参赛的,也就是在中间夹着0呢。因为0对递增是无影响的所以我完全可以想像为在len区间里塞了m个0,但是i点得在,那么就是塞了m-1个0,这样的话方案数就是C(len+m-1,m)。
好了,这样dp就解决了,因为我do时用的是i点的前j个区间,所以数组要维护前缀和,又因为我的区间是从小到大推过来的,更新了j后j-1就没有用了,所以我们可以隐去第二维,这样省很多空间。最后ans就是所有的g数组加一起,因为后面的点都可以为0的,所以对于每一个1到i都算新方案。
%%%@yspm
#include<bits/stdc++.h>
#define int long long
#define low(x) lower_bound(c+1,c+1+tot,x[i])-c
using namespace std;
const int mod=1e9+7;
int n,len,a[505],b[505],c[1015],num,sum[505]={1},C[505]={1},inv[505]={0,1},ans;
inline void fr(){freopen("c.in","r",stdin);}
inline void sc(){scanf("%lld",&n);}
namespace AYX
{ inline void work()
{ for(int i=1;i<=n;++i)scanf("%lld%lld",&a[i],&b[i]),c[++num]=a[i],c[++num]=++b[i];
for(int i=2;i<=n;++i)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
sort(c+1,c+1+num);
int tot=unique(c+1,c+1+num)-c-1;
for(int i=1;i<=n;++i)
a[i]=low(a),b[i]=low(b);
for(int j=1;j<tot;++j)
{ int len=c[j+1]-c[j];
for(int i=1;i<=n;++i)C[i]=(((C[i-1]*(len+i-1))%mod)*inv[i])%mod;
for(int i=n;i;--i)
{ if(a[i]<=j and b[i]>=j+1)
{ int f=0,m=1,cc=len;
for(int k=i-1;k>=0;--k)
{ f=(f+cc*sum[k])%mod;
if(a[k]<=j and b[k]>=j+1)cc=C[++m];
}
sum[i]=(sum[i]+f)%mod;
}
}
}
for(int i=1;i<=n;++i)ans=(ans+sum[i])%mod;
printf("%lld",ans);
}
inline short main()
{sc();work();return 0;}
}
signed main()
{return AYX::main();}