题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=2429
题目大意:给n个串,如果某个串最后一个字母等于另一个字符第一字母就可以相连。现在从这些串中选一个S作为初始点,两个人轮流取n个串作为S的下一个串,如果最后选的是字符串T,那么最后的选的人就赢了。问进行不多于m轮,第一个选的人赢的种数,对MOD=10001取余。
解题思路:矩阵乘法。题目要我们做的是统计进行1轮,2轮,3轮..m轮的总种数,而且要第一个赢的种数。而转换后的模型就是给定一张有向图也可以说是可达阵,问选择x次后是第一个人赢的总种数。
因为要第一个人赢,所以选的次数为1,3...n等奇数的时候才能累加。
设矩阵为A,问题就变成求A^1 + A^3 + A^5..A^k(k为小等于m的最后一个奇数).这样不好求,如果能转换成A^1+A^2..A^n这样的式子就好好求了,套个矩阵的模板就ok了。再转换下,A^1 + A^1 * ( A^2 + A^4 .. + A^(k-1)),将A^2看成一个矩阵B,则原式子变成A^1 + A^1*(B^1+B^2+B^3..B^s) (s = (k-1)/2),这样就可以用经典的二分+矩阵乘法的方法做了。
下午的训练卡这题卡了4个半小时,我*************************.杭电用windows系统的服务器,系统栈小得可怜,结果我RE了一下,赛后拿别人曾经ac的代码去提交,竟然也RE了,沧海桑田,杭电是越来越节约了。最后因祸得服偷学了一个非递归的写法,成功AC。
测试数据:
10
3
abc
cac
cde
abc
cde
1
Out: 1
3
abc
cac
cde
abc
cde
2
Out:1
3
abc
cac
cde
abc
cde
3
Out:2
3
abc
cac
cde
abc
cde
4
Out:2
C艹代码:
#pragma comment(linker, "/STACK:1024000000,1024000000")
#include <stdio.h>
#include <string.h>
#define MAX 100
#define MOD 10001
#define int64 int//long long//__int64
int n, begin, end;
int64 m,final[MAX][MAX];
char str[MAX][MAX],tp[MAX];
struct Mat {
int mat[MAX][MAX], size;
friend Mat operator *(Mat a, Mat b);
friend Mat operator +(Mat a, Mat b);
friend Mat operator ^(Mat a, int64 k);
} E;
Mat operator *(Mat a, Mat b) {
Mat c;
c.size = a.size;
memset(c.mat, 0, sizeof (c.mat));
for (int i = 0; i < c.size; ++i)
for (int j = 0; j < c.size; ++j)
for (int k = 0; k < c.size; ++k)
if (a.mat[i][k] && b.mat[k][j])
c.mat[i][j] = (c.mat[i][j] + a.mat[i][k] * b.mat[k][j]) % MOD;
return c;
}
Mat operator +(Mat a, Mat b) {
Mat c;
c.size = 2 * n;
for (int i = 0; i < n; ++i)
for (int j = 0; j < n; ++j)
c.mat[i][j] = (a.mat[i][j] + b.mat[i][j]) % MOD;
return c;
}
Mat operator ^(Mat a, int64 k) {
Mat c = E;
c.size = a.size;
while (k) {
if (k & 1) c = c * a;
a = a * a, k >>= 1;
}
return c;
}
Mat sum1(Mat A,int k) {
//空间复杂度为O(n*n),时间复杂度为O(log(k)*n^3)
if (k == 1) return A;
if (k & 1) return sum1(A,k-1) + (A^k);
return sum1(A,k/2) * ((A^(k / 2)) + E);
}
Mat sum2(Mat A, int64 k) {
int i,j;
Mat c = A;
c.size = 2 * A.size;
for (i = 0; i < n; ++i)
c.mat[i][i+n] = c.mat[i+n][i+n] = 1;
c = c^(k+1);
Mat temp;
temp.size = n;
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
temp.mat[i][j] = (i==j?c.mat[i][j+n]-1:c.mat[i][j+n]);
return temp;
}
int Solve() {
int i, j;
Mat c;
E.size = c.size = n;
memset(c.mat, 0, sizeof (c.mat));
memset(E.mat, 0, sizeof (E.mat));
for (i = 0; i < 2 * n; ++i) E.mat[i][i] = 1;
for (i = 0; i < n; ++i)
for (j = 0; j < n; ++j)
if (str[i][strlen(str[i]) - 1] == str[j][0])
c.mat[i][j] = 1;
if (m % 2 == 0) m--;
int cnt = (m - 1) / 2;
Mat c2 = c * c;
int ans = 0;
c = c + c * sum2(c2,cnt);
ans = (ans + c.mat[begin][end]) % MOD;
return ans;
}
int main()
{
int i, j, k,t;
scanf("%d", &t);
while (t--) {
scanf("%d", &n);
for (i = 0; i < n; ++i)
scanf("%s",str[i]);
scanf("%s",tp);
for (i = 0; i < n; ++i)
if (strcmp(tp,str[i]) == 0)
begin = i;
scanf("%s",tp);
for (i = 0; i < n; ++i)
if (strcmp(tp,str[i]) == 0)
end = i;
scanf("%d",&m);
int ans = Solve();
printf("%d\n",ans%MOD);
}
}
本文ZeroClock原创,但可以转载,因为我们是兄弟。