CF101B Buses
CF101B Buses
题意翻译
Gerald家住在离学校很远的地方,他每天上学都要做公交车。 Gerald家被标为0号车站,学校被标为n号车站,再Gerald家和学校之间还有n-1个车站。 在Gerald家和学校之间,有m辆公交车。第i辆公交车从第si个车站驶往第ti个车站。Gerald可以在第si个车站到第(ti-1)个车站之间任意一个车站上这辆公交车,但是只能在第ti个车站下车。 Gerald不能在两个车站中间走动,也不能沿返回的路线走。 问Gerald有多少种不同的从家到学校的方法。输出答案模1e9+7。 标准输入 两个数字n和m,表示站台的个数和公交车的个数 接下来m行每行两个数字,为si和ti 标准输出 输出一个数字,即本题的答案。
题解:
计数类题想DP。
首先想:状态可以设成\(dp[i]\)表示到车站\(i\)下车有多少种方式。答案是\(dp[n]\)。但是发现\(n\)很大,而且不太好转移。所以开始思考:到车站\(i\)下车,只能是终点在\(i\)的车次。既然\(m\)的范围可以,又有这个性质,能不能直接用车次代替位置来维护呢?
可以:设状态为\(dp[i]\)表示第\(i\)趟车(当然要在\(t[i]\)下车)的方案数,最终答案是所有\(t[i]=n\)的\(dp[i]\)的和。转移也非常好想:能在\(t[i]\)下车,一定会在\(s[i]-t[i]\)这段路上上车,所以只需要枚举有哪些车次的终点在这段路上,这些车都可以转移到当前状态。
那就麻烦了,怎么找到这些转移的车次呢?
我们可以考虑对其按右端点进行排序,这样,转移而来的那些车次一定在当前车次之前。那么,就可以通过二分查找来找到这个东西。
代码:
#include<cstdio>
#include<vector>
#include<algorithm>
using namespace std;
const int maxm=1e5+5;
const int mod=1e9+7;
int n,m,ans;
vector<pair<int,int> >e;
vector<int> f;
int dp[maxm],sum[maxm];
int main()
{
scanf("%d%d",&n,&m);
e.push_back(make_pair(0,0));
f.push_back(0);
for(int i=1;i<=m;i++)
{
int s,t;
scanf("%d%d",&s,&t);
e.push_back(make_pair(t,s));
f.push_back(t);
}
sort(e.begin(),e.end());
sort(f.begin(),f.end());
for(int i=1;i<=m;i++)
{
int x=e[i].second,y=e[i].first;//x,y是当前路线的起终点
if(!x)
dp[i]=1;
int s=lower_bound(f.begin(),f.end(),x)-f.begin();
int t=lower_bound(f.begin(),f.end(),y)-f.begin();
dp[i]=(dp[i]+sum[t]-sum[s]+mod)%mod;
sum[i+1]=(sum[i]+dp[i])%mod;
}
for(int i=1;i<=m;i++)
if(e[i].first==n)
ans=(ans+dp[i])%mod;
printf("%d\n",ans);
return 0;
}