题目链接: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原创,但可以转载,因为我们是兄弟。