第 19 次 CCF CSP 认证 202006-4 1246(digits)

目录

    • 题目
    • 方法一(暴力法:28分)
        • 思路分析
        • 代码
    • 方法二(96分)

题目

第 19 次 CCF CSP 认证 202006-4 1246(digits)_第1张图片
第 19 次 CCF CSP 认证 202006-4 1246(digits)_第2张图片

方法一(暴力法:28分)

思路分析

  1. 肯定要先发现数字的规律:
    第 19 次 CCF CSP 认证 202006-4 1246(digits)_第3张图片
    我们以第8行为例,数字由三段构成
  • 第一段绿色:是i-3的数字串
  • 第二段蓝色:是i-1的去除前几位剩余的数字串,去除的位数等于i-4数字串的位数
  • 第三段黄色:是i-2的数字串
    得到数字串的规律:str[i] = str[i - 3] + str[i - 1].substr(str[i - 4].size()) + str[i - 2];
  1. 在字符串中匹配n时用到string.find()函数

这样出现的限制主要在空间方面,内存不够储存数字串

代码

#include
#include
using namespace std;

int main()
{
     
	int n;
	string s_num;
	cin >> n >> s_num;
	string str[1010];
	str[1]="2";
	str[2]="4";
	str[3]="16";
	str[4]="264";
	int ans=0, position=0;
	//生成数字串
	for(int i=5;i<=n;i++)
		str[i]=str[i-3]+str[i-1].substr(str[i-4].size())+str[i-2];
	string str_n = str[n];
	while ((position = str_n.find(s_num, position)) != string::npos)
	{
     
		position++;
		ans++;
		if(ans>998244353)
			ans%=998244353;
	}
	cout<<ans;
	return 0;		
 } 

在这里插入图片描述

方法二(96分)

第一种暴力方法差强人意,我们换个角度
第 19 次 CCF CSP 认证 202006-4 1246(digits)_第4张图片
注意到有24个测试点竟然只是 ∣ S ∣ = 1 |S|=1 S=1 ∣ S ∣ = 2 |S|=2 S=2

1.考虑一位,只有1,2,4,6四种情况,

  • 1 -》2
  • 2 -》 4
  • 4 -》 1 或 6
  • 6 -》 6 或 4

2.考虑两位,根据前10个数字串发现,无非 16 , 26 , 41 , 44 , 46 , 61 , 62 , 64 , 66 , 42 16,26,41,44,46,61,62,64,66,42 16264144466162646642这10种

  • 16 -》26
  • 26 -》 46
  • ……

依次给14个数编号
d p [ ] [ 1 ] = 1 , d p [ ] [ 1 ] = 2 , d p [ ] [ 3 ] = 4 , d p [ ] [ 4 ] = 6 , d p [ ] [ 5 ] = 16 , d p [ ] [ 6 ] = 26 , d p [ ] [ 7 ] = 41 dp[][1]=1,dp[][1]=2 ,dp[][3]=4 ,dp[][4]=6, dp[][5]=16,dp[][6]=26 ,dp[][7]=41 dp[][1]=1,dp[][1]=2,dp[][3]=4,dp[][4]=6,dp[][5]=16,dp[][6]=26,dp[][7]=41……

那我们可以根据线性递推的DP公式写出变换矩阵(根据线性递推的DP公式如何写出变换矩阵)

#include
#define ll long long
using namespace std;
const int mod = 998244353;

//矩阵快速幂模板 
struct mat
{
     
    ll m[15][15];
}unit,a;
void init()
{
     
    for(int i=1;i<15;i++)
        unit.m[i][i]=1;
}
mat matmul(mat a,mat b)//c=矩阵a*矩阵b
{
     
    mat c;
    ll tmp =0;
    for(int i=1;i<15;i++)
        for(int j=1;j<15;j++)
        {
     
            tmp=0;
            for(int k=1;k<15;k++)
            {
     
                tmp=(tmp+a.m[i][k]*b.m[k][j])%mod;
            }
            c.m[i][j]=tmp;
        }
    return c;
}
mat QuickPow(mat a,ll n)//ans=矩阵a^n
{
     
	for(int i=1;i<15;i++)
    	unit.m[i][i]=1;
    mat ans=unit;
    while(n)
    {
     
        if(n&1)
            ans=matmul(ans,a);
        a = matmul(a,a);
        n>>=1;
    }
    return ans;
}
//对S=1和S=2写入编号 
map<string,int>mp;
void init_1()
{
     
    mp["1"] = 1; mp["2"] = 2; mp["4"] = 3; mp["6"] = 4;
    mp["16"] = 5; mp["26"] = 6; mp["41"] = 7; mp["44"] = 8;
    mp["46"] = 9; mp["61"] = 10; mp["62"] = 11; mp["64"] = 12;
    mp["66"] = 13; mp["42"] = 14;
};

int main()
{
     
	mat a=
	{
     {
     
	    {
     0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},  //0
	    {
     0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},  //1
	    {
     0,1,0,0,0,0,0,0,0,0,0,0,0,0,0},  //2
	    {
     0,0,1,0,1,0,0,0,0,0,0,0,0,0,0},  //3
	    {
     0,0,0,1,1,0,0,0,0,0,0,0,0,0,0},  //4
	    {
     0,0,0,1,0,0,0,0,0,0,0,0,0,0,0},  //5
	    {
     0,0,0,0,0,1,0,0,0,0,0,0,0,0,0},  //6
	    {
     0,0,0,0,0,0,0,0,0,0,0,0,1,0,0},  //7
	    {
     0,0,0,0,0,0,0,0,0,0,0,1,0,0,0},  //8
	    {
     0,0,0,0,0,0,1,0,0,0,0,0,0,1,0},  //9
	    {
     0,0,0,0,0,0,0,0,1,0,0,0,0,0,0},  //10
	    {
     0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},  //11
	    {
     0,0,0,0,1,0,0,0,0,0,0,0,0,0,1},  //12
	    {
     0,0,0,0,0,0,0,0,0,1,0,0,0,0,0},  //13
	    {
     0,0,0,0,0,0,0,0,0,0,1,0,0,0,0}   //14
    }};

    init_1();
	int n;
	cin>>n;
	mat ans=QuickPow(a,n);
	
	string s;
    cin>>s;
    int t = mp[s];
    ll answer = 0;
    answer = (ans.m[t][1])%mod;
    cout<<answer<<endl;
}

你可能感兴趣的:(CCF,CSP认证)