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;
}
posted @ 2020-11-29 18:04  Seaway-Fu  阅读(98)  评论(0编辑  收藏  举报