都市环游


【问题描述】
因为 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。
【输入输出样例】
travel.in travel.out
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
第 5 页 共 6 页
【数据规模和约定】
对于 20%的数据,n<=10,t<=80;
对于 50%的数据,n<=30,t<=80;
对于 100%的数据,n<=70,m<=1000,t<=100000000,hi<=70。

 

题解:
  首先一个二维的dp就可以搞出来,就是设dp[i][j],表示处于i节点,已经用了j天的方案数,转移十分显然,能否走或者停留到原点,所以dp[i][j]=dp[i][j-1],dp[i][j]+=dp[to][j-1],这个to,是反边的儿子节点,就是说可以从to转移到i来。这个只能有50分,记忆化搜索跑的十分快。

  但我们注意到t太大了,根本开不下,所以我们思考,因为城市的限制很小,所以一旦大于71,后面的转移就不用考虑车子了,所以前面的71可以用dp处理出来,后面的我们可以用矩阵快速幂来优化。

  首先,初始矩阵s[i][j],表示第72天i到j的方案数,然后转移矩阵k[i][j],表示i~j的路径数,然后乘起来就对了,但为什么是对的呢?其实本质就是一个floyed和乘法原理求方案数。

 

代码:

#include <cstdio>
#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <iostream>
#define MAXN 100
#define mod 10086
#define MAXN2 1000000
using namespace std;
struct zhen{
    int w[MAXN][MAXN];
    zhen(){
        memset(w,0,sizeof(w));
    }
};
int c[MAXN][MAXN],ask[MAXN],dp[MAXN][MAXN];
int n,m,t;

void work(){
    dp[1][0]=1;
    for(int t=0;t<=80;t++)
        for(int i=1;i<=n;i++)
            for(int j=1;j<=n;j++)
                if(t+1>=ask[j])
                    dp[j][t+1]+=dp[i][t]*c[i][j],dp[j][t+1]%=mod;
    return;
}

zhen jvchen(zhen x,zhen y){
    zhen z;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=n;j++)
            for(int k=1;k<=n;k++)
                z.w[i][j]+=x.w[i][k]*y.w[k][j],z.w[i][j]%=mod;
    return z;
}


int main(){
    scanf("%d%d%d",&n,&m,&t);
    for(int i=1;i<=n;i++) scanf("%d",&ask[i]),c[i][i]++;
    for(int i=1;i<=m;i++){
        int x,y;scanf("%d%d",&x,&y);
        c[x][y]++;
    }
    work();
    if(t<=79){
        printf("%d",dp[n][t]);
        return 0;
    }
    zhen star,zhuan;
    for(int i=1;i<=n;i++){
        star.w[i][i]=dp[i][80];
        for(int j=1;j<=n;j++)
            zhuan.w[i][j]=c[i][j];
    }
    t-=80;
    while(t){
        if(t&1) star=jvchen(star,zhuan);
        zhuan=jvchen(zhuan,zhuan);t>>=1;
    }
    int ans=0;
    for(int i=1;i<=n;i++) ans+=star.w[i][n],ans%=mod;
    printf("%d",ans);
    return 0;
}

 

posted @ 2017-08-17 21:36  人间失格—太宰治  阅读(200)  评论(0编辑  收藏  举报