【NOIP2014八校联考第1场第2试9.21】都市环游

2 篇文章 0 订阅
2 篇文章 0 订阅

Description

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

Input

第一行三个数n,m,t,表示有n个城市m条道路t时间。
第二行n个数,hi表示第i个城市的道路性能要求。
第三到m+2行,每行两个数u,v,表示城市u与城市v之间有一条单向道路连接(可能有重边)。

Output

包括一个数字,表示旅游的方案模10086。

Sample Input

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

Sample Output

245

Data Constraint

对于20%的数据,n<=10,t<=80;
对于50%的数据,n<=30,t<=80;
对于100%的数据,n<=70,m<=1000,t<=100000000,hi<=70。

The Solution

很容易想到这是个Dp
我们设f[i][j]表示第i时刻,到达第j点的方案总数。
那么随手推一推我们可以得到
f[i][j]=f[i1][k]a[k][j]
其中a[k][j]表示k到j的路径数。
但是这样做显然不能拿到满分
满分做法的话也很简单,做一下矩阵乘法就好了
考虑到h<=70,那么我们可以分段打程序
当t<=70时,暴力dp即可
否则则做矩乘。

CODE

#include <cstdio>
#include <iostream>
#include <cmath>
#include <algorithm>
#include <cstring>
#define fo(i,a,b) for (int i=a;i<=b;i++)
#define fd(i,a,b) for (int i=a;i>=b;i--)
#define N 75
#define INF 1 << 30
#define mo 10086

using namespace std;


typedef int arr[N][N];
arr f,a,g;
int h[1005];
int n,m,t;

int read(int &n)
{
    char ch = ' ';
    int q = 0, w = 1;
    for (;(ch != '-') && ((ch < '0') || (ch> '9'));ch = getchar());
    if (ch == '-') w = -1,ch = getchar();
    for (; ch >= '0' && ch <= '9';ch = getchar()) q = q * 10 + ch - 48;
    n = q * w;
    return n;
}

namespace ib {char b[100];}

inline void pint(int x)
{
    if (x == 0)
    {
        putchar(48);
        return;
    }
    if (x < 0) 
    {
        putchar('-');
        x = -x;
    }
    char *s = ib :: b;
    while (x) *(++ s) = x % 10,x /= 10;
    while (s != ib :: b) putchar((* (s --)) + 48);
}

void Mult(arr &x,arr &y,arr &ans)
{
    arr t;
    memset(t,0,sizeof(t));
    fo(k,1,n)
        fo(i,1,n)
            if (x[i][k])
            fo(j,1,n)
                t[i][j] = (t[i][j] + x[i][k] * y[k][j]) % mo;
    memcpy(ans,t,sizeof(ans));
}

void Pow(int n,arr &ans)
{
    for (;n;n >>= 1,Mult(a,a,a))
        if (n & 1) Mult(ans,a,ans);
}

int main()
{
    freopen("xor4.in","r",stdin);
    read(n),read(m),read(t);
    fo(i,1,n) read(h[i]);
    fo(i,1,m)
    {
        int x,y;
        read(x),read(y);
        a[x][y] ++;
    }
    fo(i,1,n) a[i][i] ++;
    f[0][1] = 1;
    fo(i,1,70)
        fo(j,1,n)
            fo(k,1,n)
                if (i >= h[k] && a[j][k]) f[i][k] = (f[i][k] + f[i - 1][j] * a[j][k]) % mo;
    if (t <= 70) pint(f[t][n]);
    else
    {
        fo(i,1,n) g[i][i] = 1;
        t -= 70;
        Pow(t,g);
        fo(i,1,n)
            fo(k,1,n) f[71][k] = (f[71][k] + f[70][i] * g[i][k] + mo) % mo;
        pint(f[71][n]);
    }
    return 0;   
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值