По заданному
ориентированному графу g необходимо определить количество разных циклов,
имеющих длину меньше k. Поскольку это
количество может быть большим, вычислить его следует по модулю m. Циклом называется непустая
последовательность вершин (необязательно различных), в которой из каждой
предыдущей вершины в следующую ведет ребро, а также существует ребро, ведущее
из последней вершины в первую. Два цикла считаются различными, если
последовательности вершин, их определяющие, разные.
Вход. Первая строка содержит количество вершин в графе n (1 ≤ n ≤ 35) и числа k
(1 ≤ k ≤ 106)
и m (1 ≤ m ≤ 109). Следующие n строк описывают граф: j
-ый символ i - ой строки матрицы
смежности указывает на присутствие ребра, ведущего из вершины i в вершину j ('Y' означает, что ребро есть, 'N' означает, что нет).
Выход. Вывести одно число – количество разных
циклов в g, длины которых меньше чем k.
Вывести результат по модулю m.
Пример
входа |
Пример
выхода |
4 6 100 NYNY NNYN YNNN YNNN |
12 |
графы
Анализ алгоритма
Теорема. Пусть А – матрица смежности графа.
Тогда Ak[i][j]
содержит количество путей, ведущих из i
-ой вершины в j -ую, и имеющих длину k. Число циклов, имеющих длину k, равно сумме диагональных элементов
матрицы Ak.
Определение. Следом
матрицы называется сумма ее диагональных элементов.
Количество
разных циклов длины, меньшей k, равно
сумме диагональных элементов матрицы S = A + A2 + A3 + …
+ Ak-1.
Возведение в степень Ak
можно совершить за O(n3log
k), где n – размер матрицы А (умножение двух матриц размера n совершается за O(n3)). Рассмотрим алгоритм вычисления матрицы S за время
O(n3log2 k). В нем следует O(log k) раз возводить матрицу A в степень k.
Пусть функция
sum(k) вычисляет значение суммы A + A2
+ … + Ak, а pow(k) находит Ak.
Если k четное, то
sum(k) = A + A2 + … + Ak = A + A2 + … + Ak/2 + Ak/2+1 + … + Ak
=
= (A + A2
+ … + Ak/2) + Ak/2 * (A + A2
+ … + Ak/2) =
= sum(k / 2) * pow(k / 2) + sum(k / 2)
Если k нечетное, то
sum(k) = A + A2 + … + Ak =
(A + A2
+ … + Ak – 1) *
A + A = sum(k – 1) * A + A
Ответом на
поставленную задачу будет значение следа матрицы sum(k – 1).
Указанную сумму
можно вычислить и по-другому. Построим матрицу B размера 2n * 2n:
B =
Тогда
Bk =
Реализация алгоритма
Переопределим
типы линейного массива vi и матрицы (двумерного целочисленного массива) vvi. Матрицу
смежности сохраняем в массив строк g.
typedef vector<int>
vi;
typedef vector<vector<int> > vvi;
vector<string>
g;
char line[50];
Сложение матриц a и b по модулю mod.
vvi add(vvi &a, vvi &b, int
mod)
{
int i, j, n =
a.size();
vvi c(n, vi(n));
for(i = 0; i
< n; i++)
for(j = 0;
j < n; j++)
c[i][j] = (a[i][j] + b[i][j]) % mod;
return c;
}
Умножение матриц a и b по модулю mod.
vvi mult(vvi &a, vvi &b, int
mod)
{
int i, j, k,
s, n = a.size();
vvi c(n, vi(n));
for(i = 0; i
< n; i++)
for(j = 0;
j < n; j++)
{
for(s = k
= 0; k < n; k++)
s = (s + (long long)a[i][k] * b[k][j]) % mod;
c[i][j] = s;
}
return c;
}
Вычисление матрицы ak по модулю mod.
vvi pow(vvi &a,
int k, int mod)
{
int i, n =
a.size();
vvi res(n, vi(n, 0));
for(i = 0; i
< n; i++) res[i][i] = 1;
while (k >
0)
{
if (k % 2)
res = mult(res, a, mod);
a = mult(a, a, mod);
k /= 2;
}
return res;
}
Вычисление суммы sum(k) = a + a2 + a3 + … + ak.
vvi sum(vvi a, int k, int mod)
{
if (k == 1) return a;
if (k % 2)
{
vvi temp = sum(a,k-1,mod);
return
add(mult(temp,a,mod),a,mod);
}
vvi temp = sum(a,k/2,mod);
vvi powk_2 = pow(a,k/2,mod);
return
add(mult(temp,powk_2,mod),temp,mod);
}
Функция countTours возвращает количество разных циклов в графе
g, длины которых меньше чем k, взятое
по модулю m.
int countTours(vector<string> &g,
int k, int m)
{
int i, j, n =
g.size();
vvi mas(n, vi(n, 0));
long long res;
Преобразуем матрицу смежности из
массива строк g в двумерный целочисленный массив mas.
for(i = 0; i
< n; i++)
for(j = 0;
j < n; j++)
if
(g[i][j] == 'Y') mas[i][j] = 1; else mas[i][j] = 0;
res = 0;
В матрице temp вычислим сумму a + a2 + a3 + … + ak-1. Все вычисления проводим по модулю m.
vvi temp = sum(mas,k-1,m);
В переменной res находим след матрицы temp.
for(i = 0; i
< n; i++)
res = (res + temp[i][i]) % m;
return res;
}
Основная часть программы.
int main(void)
{
scanf("%d %d
%d\n",&n,&k,&m);
g.clear();
for(i = 0; i
< n; i++)
{
gets(line);
g.push_back(line);
}
res = countTours(g,k,m);
printf("%d\n",res);
}