码迷,mamicode.com
首页 > 其他好文 > 详细

【题解】论逼格

时间:2020-02-27 23:40:14      阅读:93      评论:0      收藏:0      [点我收藏+]

标签:read   inter   char   getc   namespace   main   自己的   分析   lld   

题目描述

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\times\left(s\bmod 2\right)} \]

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

输入格式

本题有多组测试数据。

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

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

输出格式

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

数据范围

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

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

分析

先考虑部分分。

\(30\;\mathtt{pts}\)

优秀的暴力模板。

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

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

\(50\;\mathtt{pts}\)

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

考虑对于长度为 \(n\) 的回文数(无前导 \(0\))的数量,应该为 \(9\times 10^{\lceil\frac{n}{2}\rceil-1}\)

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

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

\(70\;\mathtt{pts}\)

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

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

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

\(100\;\mathtt{pts}\)

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

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

还看到这个式子:
\[ \large{S'_n = 9\times \sum_{i=1}^n(2i-1)\times 10^{i-1}} \]
我们要核心解决的就是
\[ S=\sum_{i=1}^n(2i-1)\times 10^{i-1} \]
注意到这是等差与等比相乘,考虑裂项。即:
\[ 10S-S=\sum_{i=1}^n(2i-1)\times 10^i-\sum_{i=1}^n(2i-1)\times 10^{i-1}\9S=\sum_{i=1}^n(2i-1)\times 10^i-\sum_{i=0}^{n-1}(2i+1)\times 10^i \]
对上了,将两项分离开,剩下的放一起,整理得:
\[ 9S=(2n-1)\times 10^n-1-2\times\sum_{i=1}^{n-1}10^i \]
运用等比数列求和公式得:
\[ 9S=(2n-1)\times 10^n-1-2\times\dfrac{10^n-10}{9} \]
注意到 \(S'_n=9S\),所以
\[ S'_n=(2n-1)\times 10^n-1-2\times\dfrac{10^n-10}{9} \]

\(10^n\) 可以快速幂解决,而 \(\frac{1}{9}\) 可以求逆元得到。这样常数就降下来了,但复杂度还是 \(\Theta(T\log 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\n", 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\n", ans);
    }
    
    return 0;
}

后记

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

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

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

【题解】论逼格

标签:read   inter   char   getc   namespace   main   自己的   分析   lld   

原文地址:https://www.cnblogs.com/5ab-juruo/p/solution-20200225-bug.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!