妖精繁殖 题解

妖精繁殖 题解

妖精繁殖

题目描述

n n n 种小妖精,我们给这 n n n 类小妖分别编号为 1 , ⋯   , n 1,\cdots,n 1,,n

t t t 年前,一次事故造出了 n n n 只小妖(视为刚出生的,而非成熟的),这些小妖的种类互不相同。

i i i 种小妖出生后需要 y i y_i yi 年成熟,成熟后会立即产下 k i k_i ki 个蛋(小妖是无性繁殖的生物)然后死亡。将它的蛋编号为 1 , ⋯   , k 1,\cdots,k 1,,k,其中,第 j j j 个蛋需要 h i , j h_{i,j} hi,j 年孵化,孵出的小妖的类型为 g i , j g_{i,j} gi,j

请问,现在和祖先关系最远的小妖到了多少代,不考虑暂未孵出的。假设祖先是 1 1 1 代,其子辈为第 2 2 2 代,孙辈为第 3 3 3 代,以此类推。

题解

可以对题目中的关系建立一个有向图,每一个点代表一种小妖,边代表繁殖关系,边权代表第 i i i 中小妖从出生到繁殖出第 j j j 种小妖所需要的最小时间。设这个图的邻接矩阵为 G G G

d p k , i , j dp_{k,i,j} dpk,i,j 为 第 i i i 种小妖有代数为 k k k 的第 j j j 种小妖所需要的最小时间

转移:
d p k , i , j = min ⁡ 1 ≤ x ≤ n d p k − 1 , i , x + G x , j dp_{k,i,j}=\min_{1\leq x\leq n}{dp_{k-1,i,x}+G_{x,j}} dpk,i,j=1xnmindpk1,i,x+Gx,j
这样就可以使用广义矩阵乘法了

题目求的是 t t t 年后和祖先关系最远的小妖的代数,等价于求一个最大的 k k k,使得 d p k dp_k dpk 中至少有一个数小于等于 t t t

因为 t t t 非常大,所以使用倍增优化

# include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair <int, int> pii;
# define int long long
# define rd(t) read <t> ()
# define mem(a, b) memset (a, b, sizeof (a))
# define fi first
# define se second
# define lc u << 1
# define rc u << 1 | 1
# define debug printf ("debug\n")
const int N = 1005; 
template <typename T> inline T read ()
{
    T s = 0; int w = 1; char c = getchar (); 
    for (; !isdigit (c); c = getchar ()) { if (c == '-') w = -1; }
    for (; isdigit (c); c = getchar ()) s = (s << 1) + (s << 3) + c - '0';
    return s * w;
}

int n, t; 
int g[N], h[N]; 
struct matrix
{
	int a[105][105]; 
	matrix () { mem (a, 0x3f); } 
	matrix operator * (const matrix &b) const
	{
		matrix ans; 
		for (int i = 1; i < 105; i ++ )
		{
			for (int k = 1; k < 105; k ++ )
			{
				for (int j = 1; j < 105; j ++ )
					ans.a[i][j] = min (ans.a[i][j], a[i][k] + b.a[k][j]); 
            }
        }
        return ans; 
    }
} a, f[70]; 
bool check (matrix a)
{
	bool hav = 0;
    for (int i = 1; i < 105; i ++ )
    {
    	for (int j = 1; j < 105; j ++ )
    	{
    		if (a.a[i][j] <= t)
    		{
    			hav = 1; 
    			break; 
    		}
    	}
    	if (hav) break; 
    }
    return hav; 
}
signed main ()
{
	n = rd (int), t = rd (int); 
    for (int i = 1; i <= n; i ++ )
    {
    	int k = rd (int), y = rd (int); 
        for (int j = 1; j <= k; j ++ ) g[j] = rd (int); 
        for (int j = 1; j <= k; j ++ ) h[j] = rd (int); 
        for (int j = 1; j <= k; j ++ )
            f[1].a[i][g[j]] = min (f[1].a[i][g[j]], y + h[j]);
    }
    for (int i = 2; i <= 64; i ++ ) f[i] = f[i - 1] * f[i - 1];
    matrix a; mem (a.a, 0); 
    int ans = 0; 
    for (int i = 64; i >= 1; i -- )
    {
    	matrix tmp = a * f[i]; 
    	if (check (tmp))
        {
            ans += (1ll << (i - 1)); 
            a = tmp; 
        }
    }
    printf ("%lld\n", ans); 
    return 0;
}
  • 12
    点赞
  • 29
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值