Hdu 2429 Word Game (数学_矩阵乘法)

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值