[FJOI2018D1T3]城市路径问题

Description

杰杰是魔法界的一名传奇人物。他对魔法具有深刻的洞察力,惊人的领悟力,以及令人叹为观止的创造力。自从他从事魔法竞赛以来,短短几年时间,就已经成为世界公认的实力最强的魔法选手之一。更让人惊叹的是,他几乎没有借助外界力量,完全凭借自己的努力达到了普通人难以企及的高度。在最近的世界魔法奥林匹克竞赛上,他使用高超的魔法本领,一路过关斩将,在最后时刻一举击败了前冠军“旅行者”,获得了魔法界最高的荣耀:女神奖杯!女神奖杯可不是一个普通的奖杯,她能够帮杰杰实现一个愿望。

杰杰本着实事求是的态度,审时度势,向女神奖杯提出了自己的愿望:想要一个女性朋友。

杰杰的愿望实现了,可是女性朋友却和他不在一个城市。杰杰想要知道:如果要到达女性朋友的所在城市,有多少种方案供他选择?

杰杰所在的世界有 n n n 个城市,从 1 1 1 n n n 进行编号。任意两个城市都通过有向道路连接。

每个城市 u u u k k k 个入点权: i n [ u ] [ 1 ] , i n [ u ] [ 2 ] . . . i n [ u ] [ k ] in[u][1],in[u][2]...in[u][k] in[u][1],in[u][2]...in[u][k],有 k k k 个出点权: o u [ u ] [ 1 ] , o u [ u ] [ 2 ] . . . o u [ u ] [ k ] ou[u][1],ou[u][2]...ou[u][k] ou[u][1],ou[u][2]...ou[u][k]

对于任意两个城市 ( u , v ) (u,v) (u,v) u u u 可以等于 v v v), u u u v v v 的道路条数为 ( o u [ u ] [ 1 ] ∗ i n [ v ] [ 1 ] + o u [ u ] [ 2 ] ∗ i n [ v ] [ 2 ] + . . . + o u [ u ] [ k ] ∗ i n [ v ] [ k ] ) (ou[u][1]*in[v][1]+ou[u][2]*in[v][2]+...+ou[u][k]*in[v][k]) (ou[u][1]in[v][1]+ou[u][2]in[v][2]+...+ou[u][k]in[v][k]) 条。

杰杰有 m m m 次询问,每次询问由三元组 ( u , v , d ) (u,v,d) (u,v,d) 构成,询问从 u u u 城市通过不超过 d d d 条道路到达 v v v 城市的方案数。

为了温柔的杰杰和他的女性朋友的美好未来,帮助他解答这个问题吧。

input

第一行读入两个正整数 n n n k k k,含义如题所示。接下来 n n n 行每行 2 k 2k 2k 个整数,第 i i i 行代表第 i i i 个城市,前 k k k 个整数代表 i i i 号城市的出点权,后 k k k 个整数代表i号城市的入点权: o u [ i ] [ 1 ] , o u [ i ] [ 2 ] , … , o u [ i ] [ k ] , i n [ i ] [ 1 ] , i n [ i ] [ 2 ] , … , i n [ i ] [ k ] ou[i][1],ou[i][2],…,ou[i][k],in[i][1],in[i][2],…,in[i][k] ou[i][1],ou[i][2],,ou[i][k],in[i][1],in[i][2],,in[i][k]

接下来一个整数 m m m,表示 m m m 个询问。

接下来 m m m 行,每行三个整数: u , v , d u,v,d u,v,d,询问从 u u u 城市通过不超过 d d d 条道路到达 v v v 城市的方案数。

将每个方案所经过的道路,按顺序写成一个序列(序列可以为空)。两个方案不同,当且仅当他们的道路序列不完全相同。

output

对于每个询问,输出一个方案数。由于答案可能太大,输出其除以 1000000007 1000000007 1000000007 后的余数。

Data

对于 40 % 40\% 40% 的数据, n ≤ 50 , k ≤ 20 , m ≤ 45 n \le 50,k \le 20, m \le 45 n50,k20,m45

对于另外 10 % 10\% 10% 的数据, n ≤ 1000 , k = 1 , m ≤ 45 n \le 1000, k = 1, m \le 45 n1000,k=1,m45

对于 100 % 100\% 100% 的数据, n ≤ 1000 , k ≤ 20 , m ≤ 50 n \le 1000, k \le 20, m \le 50 n1000,k20,m50

Solution

很明显的矩阵快速幂,发现如果是整个矩阵拿去搞的话 100 0 3 1000^3 10003 就爆炸了,考虑可以把转移拆开,只有第一次和最后一次是 1000 × 20 1000\times20 1000×20 的转移,中间的都是 20 × 20 20\times 20 20×20 的转移,直接做就好了。

Code

#include <algorithm>
#include <cstdio>
#include <cmath>
#include <cstdlib>
#include <cstring>
#include <ctime>
#include <iostream>
#include <queue>
#include <set>
#include <stack>

#define R register
#define ll long long
#define db double
#define sqr(_x) (_x) * (_x)
#define Cmax(_a, _b) ((_a) < (_b) ? (_a) = (_b), 1 : 0)
#define Cmin(_a, _b) ((_a) > (_b) ? (_a) = (_b), 1 : 0)
#define Max(_a, _b) ((_a) > (_b) ? (_a) : (_b))
#define Min(_a, _b) ((_a) < (_b) ? (_a) : (_b))
#define Abs(_x) (_x < 0 ? (-(_x)) : (_x))

using namespace std;

namespace Dntcry
{
	inline int read()
	{
		R int a = 0, b = 1; R char c = getchar();
		for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
		for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
		return a * b;
	}
	inline ll lread()
	{
		R ll a = 0, b = 1; R char c = getchar();
		for(; c < '0' || c > '9'; c = getchar()) (c == '-') ? b = -1 : 0;
		for(; c >= '0' && c <= '9'; c = getchar()) a = (a << 1) + (a << 3) + c - '0';
		return a * b;
	}
	const int Maxn = 1010, Maxk = 21, Mod = 1000000007;
	int n, p, m, u, v, d, tmp, in[Maxk][Maxn], out[Maxn][Maxk];
	struct Matrix
	{
		int v[Maxk][Maxk];
		Matrix() { memset(v, 0, sizeof(v)); }
		Matrix operator + (const Matrix &b) const
		{
			R Matrix c;
			for(R int i = 1; i <= p; i++)
				for(R int j = 1; j <= p; j++)
					c.v[i][j] = (v[i][j] + b.v[i][j]) % Mod;
			return c;
		}
		Matrix operator * (const Matrix &b) const
		{
			R Matrix c;
			for(R int i = 1; i <= p; i++)
				for(R int k = 1; k <= p; k++) if(v[i][k])
					for(R int j = 1; j <= p; j++) if(b.v[k][j])
						c.v[i][j] = (c.v[i][j] + 1ll * v[i][k] * b.v[k][j] % Mod) % Mod;
			return c;
		}
	}E, A, Now, Ans;
	void Calc(R int x)
	{
		if(x == 1) { Now = Ans = A; }
		else 
		{
			Calc(x >> 1);
			Ans = Ans * (E + Now), Now = Now * Now;
			if(x & 1) Now = Now * A, Ans = Ans + Now;
		}
	}
	int Main()
	{
		n = read(), p = read();
		for(R int i = 1; i <= n; i++)
		{
			for(R int j = 1; j <= p; j++) out[i][j] = read() % Mod;
			for(R int j = 1; j <= p; j++) in[j][i] = read() % Mod;
		}
		for(R int i = 1; i <= p; i++) E.v[i][i] = 1;
		for(R int i = 1; i <= p; i++)
			for(R int k = 1; k <= n; k++) if(in[i][k])
				for(R int j = 1; j <= p; j++) if(out[k][j])
					A.v[i][j] = (A.v[i][j] + 1ll * in[i][k] * out[k][j] % Mod) % Mod;
		m = read();
		while(m--)
		{
			u = read(), v = read(), d = read();
			tmp = (u == v);
			if(d) for(R int i = 1; i <= p; i++) tmp = (tmp + 1ll * out[u][i] * in[i][v] % Mod) % Mod;
			if(d > 1)
			{
				Calc(d - 1);
				for(R int i = 1; i <= p; i++)
					for(R int j = 1; j <= p; j++)
						tmp = (tmp + 1ll * out[u][i] * in[j][v] % Mod * Ans.v[i][j] % Mod) % Mod;
			}
			printf("%d\n", tmp);
		}
		return 0;
	}
}
int main()
{
	return Dntcry :: Main();
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值