【多校第三场】【矩阵快速幂】 HDU 5318 The Goddess Of The Moon

6 篇文章 0 订阅

题目地址:http://acm.hdu.edu.cn/showproblem.php?pid=5318


这道题当时并没有看。。。因为我们队弱的只能5小时切水orz。。。首先关于这个题,我们先把重复的数字去除掉,然后建立一个变形用的change矩阵记录变化方式。。。矩阵快速幂部分的内容只能多刷一些题目来学习


其实有点不想详细讲。。。然而还是写了,绝对不是因为傲娇什么的。。。

首先,我们定义当前以第 i 根锁链结尾的数量为mat_cont [ i ];可以得到一组数字mat_cont;

然后,因为每种绳子都是无限的,所以当前以第 i 根绳子结尾的时候所能连接的绳子是一样的。

根据矩阵相乘的公式,如果第 i 根后面能接第 j 根,那么只要把 change[ i ][ j ]变成1就可以了。

语文并没有学好所以如果有什么说的不清楚的地方请加油自行脑补。。。


#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>

using namespace std;

int num[55];
const int mod=1000000007;

struct mat
{
    int x;
    int num[55][55];
    mat operator*(mat x)
    {
        mat ans;
        for(int i=0;i<x.x;i++)
        {
            for(int j=0;j<x.x;j++)
            {
                int temp=0;
                for(int k=0;k<x.x;k++)
                {
                    temp=(int)((temp+(long long)num[i][k]*x.num[k][j])%mod);
                }
                ans.num[i][j]=temp;
            }
        }
        ans.x=x.x;
        return ans;
    }
}change;
int mat_cont[2][55];
int mat_num[55];

void show(int n)
{
    for(int i=0;i<n;i++)
    {
        for(int j=0;j<n;j++)
        {
            cout<<change.num[i][j]<<" ";
        }
        cout<<endl;
    }
}

bool judge(int a,int b)
{
    int k=1,n=b;
    while(n)
    {
        n/=10;
        k*=10;
    }
    //cout<<k<<endl;
    k/=100;
    int t=100;
    while(1)
    {
        if(a%t==b/k) return true;
        if(a%t==a || b/k==b) break;
        k/=10;
        t*=10;
    }
    return false;
}

int main()
{
	int T;
	scanf("%d",&T);
	while(T--)
	{
	    int n,m;
	    scanf("%d%d",&n,&m);
	    for(int i=0;i<n;i++) scanf("%d",&num[i]);

	    sort(num,num+n);
	    memset(change.num,0,sizeof change.num);
	    memset(mat_cont,0,sizeof mat_cont);

	    int cont=0;
	    for(int i=0;i<n;i++)
        {
            if(num[i]<10) continue;
            if(num[i]==num[i-1])
            {
                //mat_cont[0][cont-1]++;
                continue;
            }
            mat_cont[0][cont]++;
            mat_num[cont++]=num[i];
        }
        change.x=cont;
        for(int i=0;i<cont;i++)
        {
            for(int j=0;j<cont;j++)
            {
                if(judge(mat_num[i],mat_num[j])) change.num[j][i]++;
            }
        }
//        for(int i=0;i<cont;i++) cout<<" "<<mat_num[i];
//        cout<<endl;

        //show(cont);

//        cout<<change.num[0][0]<<" ";
//        cout<<change.num[0][1]<<endl;
//        cout<<change.num[1][0]<<" ";
//        cout<<change.num[1][1]<<endl;

        int flag=1;
        m--;
        while(m)
        {
            if(m&1)
            {
                for(int i=0;i<cont;i++)
                {
                    int temp=0;
                    for(int j=0;j<cont;j++)
                    {
                        temp=(int)((temp+(long long)change.num[i][j]*mat_cont[1-flag][j])%mod);
                    }
                    mat_cont[flag][i]=temp;
                }
                flag=1-flag;
            }
            change=change*change;
            //show(cont);
            //cout<<endl;
            m>>=1;
        }

        int ans=0;
        for(int i=0;i<cont;i++)
            ans=(ans+mat_cont[1-flag][i])%mod;
        printf("%d\n",ans);
	}
	return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值