zoukankan      html  css  js  c++  java
  • 【题解】论逼格

    题目描述

    I shall dedicate myself to the interest of the country in life and death, irrespective of personal weal and woe.
    ——Lin Zexu

    最近,goldgenius 决定想办法提升自己的逼格。经过数日的研究,他发现:回文数字是非常有逼格的。为了让自己成为一个更有逼格的人,他想要知道:

    [large{S_n=sum_{i=1}^n is_i imesleft(smod 2 ight)} ]

    其中 (s_i) 是长度为 (i) 的回文数个数(不允许有前导 (0))。

    输入格式

    本题有多组测试数据。

    第一行一个正整数 (T),即测试数据组数。

    接下来 (T) 行,每行一个正整数 (n)

    输出格式

    (T) 行,每行一个整数,表示 (S_n mod 233333)

    数据范围

    测试时间限制 (1000 extrm{ms}),空间限制 (128 extrm{MiB})

    • 对于 (30\%) 的数据,(nle 5)
    • 对于另外 (20\%) 的数据,(sum nle 10^7)
    • 对于另外 (20\%) 的数据,(T=1)
    • 对于 (100\%) 的数据,(Tle 5 imes 10^5)(nle 10^9)

    分析

    先考虑部分分。

    (30;mathtt{pts})

    优秀的暴力模板。

    枚举所有 (k<10^n),判断回文即可。

    复杂度上限:(Theta(Tn imes 10^n))

    (50;mathtt{pts})

    懂点数学的都应该拿到这一部分分。

    考虑对于长度为 (n) 的回文数(无前导 (0))的数量,应该为 (9 imes 10^{lceilfrac{n}{2} ceil-1})

    为什么?度娘 or 小猿搜题给你答案。

    然后枚举一遍,从 (1)(n) 就行,复杂度 (Theta(sum n))

    (70;mathtt{pts})

    在这里,(n) 变得很大,如何快速求出一个 (n) 值呢?

    考虑递推,设 (S'_n=S_{2n-1}),则 (S'_n=S'_{n-1}+(2n-1) imes 9 imes 10^{n-1})

    可以通过矩阵快速幂得到,复杂度进一步降到 (Theta(Tlog n))

    (100;mathtt{pts})

    接下来就是无聊的卡常环节。

    矩阵快速幂常数略大,怎么优化?

    还看到这个式子:

    [large{S'_n = 9 imes sum_{i=1}^n(2i-1) imes 10^{i-1}} ]

    我们要核心解决的就是

    [S=sum_{i=1}^n(2i-1) imes 10^{i-1} ]

    注意到这是等差与等比相乘,考虑裂项。即:

    [10S-S=sum_{i=1}^n(2i-1) imes 10^i-sum_{i=1}^n(2i-1) imes 10^{i-1}\ 9S=sum_{i=1}^n(2i-1) imes 10^i-sum_{i=0}^{n-1}(2i+1) imes 10^i ]

    对上了,将两项分离开,剩下的放一起,整理得:

    [9S=(2n-1) imes 10^n-1-2 imessum_{i=1}^{n-1}10^i ]

    运用等比数列求和公式得:

    [9S=(2n-1) imes 10^n-1-2 imesdfrac{10^n-10}{9} ]

    注意到 (S'_n=9S),所以

    [S'_n=(2n-1) imes 10^n-1-2 imesdfrac{10^n-10}{9} ]

    (10^n) 可以快速幂解决,而 (frac{1}{9}) 可以求逆元得到。这样常数就降下来了,但复杂度还是 (Theta(Tlog n))

    Code

    考场上写的快速幂,不知为什么特别慢,只有 (60) 分。

    #include <cstdio>
    #include <cctype>
    #include <cstring>
    using namespace std;
    
    typedef long long ll;
    const int mod = 233333, unit_mat[4][4] = {{1, 0, 0, 0}, {0, 1, 0, 0}, {0, 0, 1, 0}, {0, 0, 0, 1}}
    , def_mat[4][4] = {{0, 0, 1, 0}, {10, 10, 0, 0}, {0, 0, 1, 0}, {0, 2, 0, 10}}, start[4][4] = {{9, 27, 0, 90}};
    
    struct mat
    {
    	ll a[4][4];
    	int l, w;
    	
    	mat()
    	{
    		memset(a, 0, sizeof(a));
    		l = w = 0;
    	}
    	
    	void setv(const int ppa[4][4], int lx, int wx)
    	{
    		l = lx, w = wx;
    		for (int i = 0; i < lx; i++)
    			for (int j = 0; j < wx; j++)
    				a[i][j] = ppa[i][j];
    	}
    	
    	void setm(const mat& m)
    	{
    		l = m.l, w = m.w;
    		
    		for (int i = 0; i < l; i++)
    			for (int j = 0; j < w; j++)
    				a[i][j] = m.a[i][j];
    	}
    };
    
    void mat_mul(const mat& au, const mat& bu, mat& cu)
    {
    	cu.w = au.w, cu.l = bu.l;
    	
    	for (int i = 0; i <	bu.l; i++)
    		for (int j = 0; j < au.w; j++)
    		{
    			cu.a[i][j] = 0;
    			for (int k = 0; k < bu.w; k++)
    				cu.a[i][j] = (cu.a[i][j] + au.a[k][j] * bu.a[i][k]) % mod;
    		}
    }
    
    inline int read()
    {
    	int ch = getchar(), n = 0, t = 1;
    	while (isspace(ch)) { ch = getchar(); }
    	if (ch == '-') { t = -1, ch = getchar(); }
    	while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
    	return n * t;
    }
    
    int main()
    {
    	int cas = read(), n, cur, pw;
    	mat ans, mul, sta, tmp;
    	
    	while (cas--)
    	{
    		n = read();
    		pw = n / 2 + (n & 1);
    		
    		mul.setv(def_mat, 4, 4);
    		sta.setv(unit_mat, 4, 4);
    		cur = 1;
    		
    		while (cur <= pw)
    		{
    			if (cur & pw)
    			{
    				mat_mul(mul, sta, tmp);
    				sta.setm(tmp);
    			}
    			
    			mat_mul(mul, mul, tmp);
    			mul.setm(tmp);
    			cur <<= 1;
    		}
    		
    		tmp.setv(start, 1, 4);
    		
    		mat_mul(sta, tmp, ans);
    		printf("%lld
    ", ans.a[0][2]);
    	}
    	
    	return 0;
    }
    

    最后搞出来的做法

    #include <cstdio>
    #include <cctype>
    using namespace std;
    
    typedef long long ll;
    const int mod = 233333, nine_inv = ######; // 你想知道9的逆元?自己求去。
    
    inline int read()
    {
    	int ch = getchar(), n = 0, t = 1;
    	while (isspace(ch)) { ch = getchar(); }
    	if (ch == '-') { t = -1, ch = getchar(); }
    	while (isdigit(ch)) { n = n * 10 + ch - '0', ch = getchar(); }
    	return n * t;
    }
    
    int main()
    {
    	int cas = read(), n, pw, cur;
    	ll mul, sta, ans;
    	
    	while (cas--)
    	{
    		n = read();
    		pw = n / 2 + (n & 1), mul = 10, cur = 1, sta = 1, ans = -1;
    		
    		while (cur <= pw)
    		{
    			if (cur & pw)
    				sta = (sta * mul) % mod;
    			
    			mul = (mul * mul) % mod;
    			cur <<= 1;
    		}
    		
    		ans = ((ans + (sta * (2 * pw - 1) % mod) - (((sta - 10 + mod) % mod) * nine_inv * 2 % mod)) + mod) % mod;
    		printf("%lld
    ", ans);
    	}
    	
    	return 0;
    }
    

    后记

    又是一道十分毒瘤的卡常题。

    这道题最难的地方,也是最精妙的地方,就是这个裂项,把乘积形式变成和形式,最后通过快速幂解决问题。

    考试时想到矩阵快速幂就以为完了,没想到这题这么卡常,虽然是一道练数学推导的好题。

  • 相关阅读:
    Android ContentProvider和getContentResolver
    onContextItemSelected 用法
    Android 控件 之 Menu 菜单
    Android
    Android Cursor类的概念和用法
    android SQLiteOpenHelper使用示例
    JAVA HashMap详细介绍和示例
    HashMap深度解析(二)
    HashMap深度解析(一)
    使用svn遇到的问题---(在编辑器没有配置svn的前提下)
  • 原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-20200225-bug.html
Copyright © 2011-2022 走看看