都市环游

题目描述
因为SJY干的奇怪事情过多,SJY收到了休假的通知,于是他准备在都市间来回旅游。SJY有一辆车子,一开始行驶性能为0,每过1时间行驶性能就会提升1点。每个城市的道路都有性能要求。SJY一共有t时间休息,一开始他位于1号城市(保证1号城市道路要求为0),他希望在n号城市结束旅程。每次穿过一条城市间的路会花费1时间,当然他也可以停留在一个城市不动而花费1时间。当且仅当车子的行驶性能大于等于一个城市,我们才能到达那里。SJY希望知道,旅游的方案模10086后的答案。(只要在某一时刻通过的道路存在一条不相同,就算不同的方案)

输入
第一行三个数n,m,t,表示有n个城市m条道路t时间。
第二行n个数,hi表示第i个城市的道路性能要求。
第三到m+2行,每行两个数u,v,表示城市u与城市v之间有一条单向道路连接(可能有重边)。
输出
包括一个数字,表示旅游的方案模10086。
样例输入
5 17 7
0 2 4 5 3
1 2
2 1
1 3
3 1
1 4
4 1
4 5
5 4
5 3
4 1
2 1
5 3
2 1
2 1
1 2
2 1
1 3
样例输出
245
提示
【数据规模和约定】
对于20%的数据,n<=10,t<=80;
对于50%的数据,n<=30,t<=80;
对于100%的数据,n<=70,m<=1000,t<=100000000,hi<=70。

Solution

补充一下题意:
1.必须要在最后时刻到达n
2.重边算不同道路
首先,我们很容易可以得到一个dp:
f[i][j]ij
但看到t最大有1亿,而n和hi却很小,所以,突破口就在于此。
试想一下,当时间达到最大速度时,意味着只要有边,你就可以任意行走。而这种告诉你两点间行走方案数,让你求t时间到某点方案数是经典的矩阵乘法模型。
所以我们可以先dp到70多秒,剩下的时间用矩阵乘法。

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
const int mod=10086;
int n,m,Time,x,y,tot,TOT,Ans;
int a[75],head[75],Next[1005],to[1005],HEAD[75],NEXT[1005],TO[1005];
int f[72][72],g[71][71],w[71][71],ans[71][71],p[71][71];
void add(int x,int y)
{
    tot++;
    Next[tot]=head[x];
    to[tot]=y;
    head[x]=tot;
}
void ADD(int x,int y)
{
    TOT++;
    NEXT[TOT]=HEAD[x];
    TO[TOT]=y;
    HEAD[x]=TOT;
}
void quick(int y)
{
    while(y>0) 
    {
        if(y%2==1) 
        {
            memset(p,0,sizeof(p));
            for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++) 
            for(int k=1;k<=n;k++) p[i][k]=(p[i][k]+g[i][j]*w[j][k])%mod;
            for(int i=1;i<=n;i++) 
            for(int j=1;j<=n;j++) g[i][j]=p[i][j];
        }
        y=y/2;
        memset(p,0,sizeof(p));
        for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++) 
        for(int k=1;k<=n;k++) p[i][k]=(p[i][k]+w[i][j]*w[j][k])%mod;
        for(int i=1;i<=n;i++) 
        for(int j=1;j<=n;j++) w[i][j]=p[i][j];
    }
    for(int i=1;i<=n;i++) Ans=(Ans+g[i][n])%mod;
    cout<<Ans;
}
int main()
{
    cin>>n>>m>>Time;
    Time++;
    for(int i=1;i<=n;i++) head[i]=HEAD[i]=-1;
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=m;i++) 
    {
        scanf("%d%d",&x,&y);
        add(x,y);
        ADD(y,x);
    }
    f[0][1]=1;
    for(int i=1;i<=71;i++)  //dp
    for(int j=1;j<=n;j++) 
    {
        f[i][j]=f[i-1][j];
        for(int t=HEAD[j];t!=-1;t=NEXT[t]) 
        if(i>a[j]) f[i][j]=(f[i][j]+f[i-1][TO[t]])%mod;
    }
    for(int i=1;i<=n;i++) //72秒时的状态
    {
        g[i][i]=f[71][i];
        for(int j=head[i];j!=-1;j=Next[j]) g[i][to[j]]=(g[i][to[j]]+f[71][i])%mod;
    }
    for(int i=1;i<=n;i++) //任意行走方案数的矩阵
    {
        w[i][i]=1;
        for(int j=head[i];j!=-1;j=Next[j]) w[i][to[j]]=(w[i][to[j]]+1)%mod;
    }
    if(Time>71) quick(Time-72); else cout<<f[Time][n];
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值