@bzoj - [email protected] [POI2015] Wycieczki

目录

  • @[email protected]
  • @[email protected]
  • @accepted [email protected]
  • @[email protected]

@[email protected]

给定一张 n 个点 m 条边的带权有向图,每条边的边权只可能是1,2,3中的一种。

将所有可能的路径按路径长度排序,请输出第 k 小的路径的长度,注意路径不一定是简单路径,即可以重复走同一个点。

input

第一行包含三个整数 n, m, k (1<=n<=40,1<=m<=1000,1<=k<=10^18)。

接下来 m 行,每行三个整数 u, v, c(1<=u,v<=n,u不等于v,1<=c<=3),表示从 u 出发有一条到 v 的单向边,边长为 c 。

可能有重边。

output

包含一行一个正整数,即第 k 短的路径的长度,如果不存在,输出-1。

sample input

6 6 11

1 2 1

2 3 2

3 4 2

4 5 1

5 3 1

4 6 3

sample output

4

sample explain

长度为1的路径有1->2,5->3,4->5。

长度为2的路径有2->3,3->4,4->5->3。

长度为3的路径有4->6,1->2->3,3->4->5,5->3->4。

长度为4的路径有5->3->4->5。

@[email protected]

一种比较直观的想法是二分答案。二分出来 mid 后我们相当于是要计算路径长度 <= mid 的路径总数。

考虑怎么求路径长度 = x 的路径数量。讨论路径中最后一条边的边权是 1, 2, 3 中的哪一个,可以由路径长度为 x - 1, x - 2, x - 3 的状态转移过来。

我们令 G1 表示这样一个矩阵,其中 G1[i][j] 为 i->j 边权为 1 的边数量。G2, G3 同理。

再定义 F[k] 表示这样一系列矩阵,其中 F[k][i][j] 为 i 到 j 路径长度为 k 的路径总数。

则有:

\[F[k] = G1*F[k-1] + G2*F[k-2] + G3*F[k-3]\]

这样,我们计算的路径长度 <= mid 的路径总数,就等于 (F[1] + F[2] + ... + F[mid]) 这样一个矩阵中所有元素之和。

观察上面那个转移式,它是一个线性递推式。我们不妨令 F[0] = I(单位矩阵),F[-1] = F[-2] = O(零矩阵)。我们可以把这个转移式写成矩阵嵌套矩阵的形式,,可以得到:

\[\begin{bmatrix}
1 & i & o1 & o1\o2 & G1 & G2 & G3\o2 & I & O & O\o2 & O & I & O\\end{bmatrix}
\begin{bmatrix}
S[k-1]\F[k-1]\F[k-2]\F[k-3]\\end{bmatrix}
=
\begin{bmatrix}
S[k]\F[k]\F[k-1]\F[k-2]\\end{bmatrix}\]

其中 S[k] 是一个统计元素和的,1*n 的向量。矩阵中的 i 是 1*n 的全 1 向量,o1 是 1*n 的全 0 向量,o2 是 n*1 的全 0 向量。

初始向量\(\begin{bmatrix} S[0]\\ F[0]\\ F[-1]\\ F[-2]\\ \end{bmatrix}\),最后将 S[mid] 的所有元素之和求出来与 k 比较大小。

这样我们就得到了一个 O(n^3log^2 k) 的算法,但并不够优秀。

考虑类比树上求某个点的第 k 个祖先的做法,即使用倍增。

记矩阵 A0 = \(\begin{bmatrix} 1 & i & o1 & o1\\ o2 & G1 & G2 & G3\\ o2 & I & O & O\\ o2 & O & I & O\\ \end{bmatrix}\),Ai = A0^(2^i)。

对 A 使用倍增即可。时间复杂度 O(n^3log k)。

最后一个问题:路径数是指数级增长的,它有可能溢出 long long。

没事儿,long long 不行我们用 double,double 不行我们用 long double。

@accepted [email protected]

#include<cstdio>
#include<iostream>
using namespace std;
const int MAXN = 40 + 5;
struct matrix{
    long double m[3*MAXN][3*MAXN];
    int r, c;
}G[64 + 5];
int n;
long double fun(matrix A) {
    long double ret = 0;
    for(int i=1;i<=n;i++)
        ret += A.m[0][i];
    return ret;
}
matrix operator * (matrix A, matrix B) {
    matrix C; C.r = A.r, C.c = B.c;
    for(int i=0;i<C.r;i++)
        for(int j=0;j<C.c;j++) {
            C.m[i][j] = 0;
            for(int k=0;k<A.c;k++)
                C.m[i][j] += A.m[i][k]*B.m[k][j];
        }
    return C;
}
int main() {
    int m; long double k;
    scanf("%d%d", &n, &m);
    cin >> k; k += n;
    G[0].r = G[0].c = 3*n + 1;
    for(int i=0;i<=n;i++) G[0].m[0][i]++;
    for(int i=1;i<=n;i++) G[0].m[i+n][i]++, G[0].m[i+2*n][i+n]++;
    for(int i=1;i<=m;i++) {
        int u, v, c; scanf("%d%d%d", &u, &v, &c);
        G[0].m[u][v+c*n-n]++;
    }
    int i;
    for(i=1;i<=65;i++) {
        G[i] = G[i-1]*G[i-1];
        if( fun(G[i]) >= k ) break;
        if( i == 65 ) {
            puts("-1");
            return 0;
        }
    }
    i--;
    matrix M = G[i];
    long long ret = 1LL<<i;
    for(;i>=0;i--) {
        if( fun(M*G[i]) < k ) {
            ret += (1LL<<i);
            M = M*G[i];
        }
    }
    printf("%lld\n", ret);
}

@[email protected]

网上一搜,全是什么拆点之类的。

心里一惊。难道我想错了。

最后发现其实通过拆点构造出来的矩阵和用递推构造出来的矩阵是一样的。

处理 long long 溢出这么复杂,还不如直接用 long double(虽然常数要大一些)

原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/10365271.html

时间: 2024-11-02 01:37:29

@bzoj - [email protected] [POI2015] Wycieczki的相关文章

@bzoj - [email&#160;protected] [POI2015] Kinoman

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 共有 m 部电影,第 i 部电影的好看值为 w[i]. 在 n 天之中每天会放映一部电影,第 i 天放映的是第 f[i] 部. 你可以选择 l, r (1 <= l <= r <= n) ,并观看第 l, l+1, -, r 天内所有的电影. 最大化观看且仅观看过一次的电影的好

@bzoj - [email&#160;protected] [POI2015] Wilcze do?y

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的序列,你有一次机会选中一段连续的长度不超过 d 的区间,将里面所有数字全部修改为 0. 请找到最长的一段连续区间,使得该区间内所有数字之和不超过 p . input 第一行包含三个整数 n, p, d (1 <= d <= n <= 2000000,0 &

@bzoj - [email&#160;protected] [POI2015] Pustynia

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一个长度为 n 的正整数序列 a,每个数都在 1 到 10^9 范围内. 告诉你其中 s 个数,并给出 m 条信息,每条信息包含三个数 l, r, k 以及 k 个正整数,表示 a[l], a[l+1], ..., a[r-1], a[r] 里这 k 个数中的任意一个都比任意一个剩

@bzoj - [email&#160;protected] [POI2015] Logistyka

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 维护一个长度为 n 的序列,一开始都是 0,支持以下两种操作: 1.U k a 将序列中第 k 个数修改为 a. 2.Z c s 在这个序列上,每次选出 c 个正数,并将它们都减去 1,询问能否进行 s 次操作. 每次询问独立,即每次询问不会对序列进行修改. input 第一行包含两个

@bzoj - [email&#160;protected] [POI2015] Myjnie

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 有 n 家洗车店从左往右排成一排,每家店都有一个正整数价格 p[i]. 有 m 个人要来消费,第 i 个人会驶过第 a[i] 个开始一直到第 b[i] 个洗车店,且会选择这些店中最便宜的一个进行一次消费.但是如果这个最便宜的价格大于 c[i],那么这个人就不洗车了. 请给每家店指定一个

@bzoj - [email&#160;protected] [POI2015] Odwiedziny

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵 n 个点的树,树上每条边的长度都为 1 ,第 i 个点的权值为 a[i]. Byteasar 会按照某个 1 到 n 的全排列 b 走 n-1 次,第 i 次他会从 b[i] 点走到 b[i+1] 点,并且这一次的步伐大小为 c[i]. 对于一次行走,假设起点为 x,终点为

@bzoj - [email&#160;protected] [POI2015] ?asuchy

目录 @[email protected] @[email protected] @accepted [email protected] @[email protected] @[email protected] 圆桌上摆放着 n 份食物,围成一圈,第 i 份食物所含热量为 c[i]. 相邻两份食物之间坐着一个人,共有 n 个人.每个人有两种选择,吃自己左边或者右边的食物.如果两个人选择了同一份食物,这两个人会平分这份食物,每人获得一半的热量. 假如某个人改变自己的选择后(其他 n-1 个人的选

@bzoj - [email&#160;protected] [Poi2011]Lightning Conductor

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @version - [email protected] @version - [email protected] @[email protected] @[email protected] 已知一个长度为

@bzoj - [email&#160;protected] [POI2014]Hotel加强版

目录 @[email protected] @[email protected] @part - [email protected] @part - [email protected] @accepted [email protected] @[email protected] @[email protected] 给定一棵树,求无序三元组 (a, b, c) 的个数,使得 dis(a, b) = dis(b, c) = dis(c, a),且 a ≠ b, b ≠ c, c ≠ a. inpu