前言

洛谷题解,懂?(

题目链接

来一点不一样的方法。

正解:动态规划 / 打表数据暴力分析

考试半小时想出方法,最后输在了两个细节上。

写一篇题解以此纪念。


打表暴力程序

最开始打的暴力对拍,没想到最后只能交这个上去了。

思路:两层循环枚举两个数,判断是否符合要求。

Code(第一种)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n;
ll ans;
bool check(int x,int y){
int c=x%10,d=y%10;
while(x>=10) x/=10;
while(y>=10) y/=10;
if(x==d&&y==c) return 1;
else return 0;
}
int main(){
//freopen("out1.out","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++){
if(!i%10) continue;
for(int j=1;j<=n;j++){
if(check(i,j)) {//printf("%d %d\n",i,j);
ans++;}
}
}
printf("%lld",ans);
return 0;
}

动态规划

这个方法很简单啊!!!

\(dp_{i,j}\) 代表以 \(i\) 开头以 \(j\) 结尾的不超过 \(n\) 的数的个数。

求一个数字首位的函数:

int one(int m){
while(m>=10) m/=10;
return m;
}

因为要保证 \(1\le m\le 9\),所以 \(dp\) 开 \(10\times 10\) 即可。

最后 \(9\times 9\) 的循环枚举满足题意的数量。

因为要求两个数的开头结尾互相对应,所以若一个数以 \(i\) 开头,以 \(j\) 结尾,那么它就有 \(dp_{j,i}\) 个数对。而这样的数一共有 \(dp_{i,j}\) 个,根据小学学的可能性总数需要用乘法,可以看出前面是 \(i\ldots j\) 数字的数对个数为 \(dp_{i,j}\times dp_{j,i}\)。答案累加就可以了。

Code(第二种)

#include<bits/stdc++.h>
using namespace std;
int n,ans,dp[10][10];
int one(int m){
while(m>=10) m/=10;
return m;
}
int main(){
scanf("%d",&n);
if(n<10){
printf("%d",n);
return 0;
}
for(int i=1;i<=n;i++) dp[one(i)][i%10]++;
for(int i=1;i<=9;i++){
for(int j=1;j<=9;j++){
ans+=dp[i][j]*dp[j][i];
}
}
printf("%d",ans);
return 0;
}

数据分析

考试想到的方法。

方法与 @CQBZJJH 相同,但是我们俩都是考试的时候想出来的,我只是调代码比他慢啊 awa!!1 这个不要 face 的人竟然说版权是他的,IEE。

我来说说这个思路是怎么出来的。

首先第一层循环肯定是枚举 \(1\sim n\),看每个数字有多少个数字对。

用第一个程序打表 \(2020\),可以得到如下的输出:Link

等等好像复制不完诶,不过没关系这点够了。

然后我们先通览全篇,然后仔细观察一下 \(1\sim 9\) 的数字对。

发现如下规律:

设 \(x\) 为 \(n\) 的首位,\(k\) 为 \(n\) 的位数。

分析:对于每个数 \(i\),因为它的数字对的那个搭档的首尾两个数字已经定下来了,所以,中间夹着的数字就可以分析出:中间没有数字的情况,中间有一个数字的情况,中间有两个数字的情况……也就是说,如果没有 \(n\) 的限制,那么这个数有的数字对的数量计算公式就是:\(10^0+10^1+10^2+\ldots\)。

但是这道题当中是有 \(n\) 的限制的(不然这道题还有什么意义呢),所以就要分析下列三种情况讨论:

1. 若 \(i \bmod 10<x\),即 \(i\) 的搭档数首位小于 \(x\)。

非常简单的情况,这个时候,中间数字数量可以从 \(0\) 取到 \(k-2\),而且不管怎么取它的搭档数都不会超过 \(n\) 的,因为它的首位小于 \(x\),而且位数不会大于 \(k\)。

所以直接:

\(ans←ans+10^{k-2}\)

即可。

2. 若 \(i \bmod 10>x\),即 \(i\) 的搭档数首位大于 \(x\)。

也是非常简单的情况,这个时候,只要此搭档数的位数等于 \(k\),就一定会大于 \(n\),此点显然易证,就不需要我多哔哔了吧?所以中间掐头去尾的数字的数量可以从 \(0\) 取到 \(k-3\),所以可以:

\(ans←ans+10^{k-3}\)。

3. 若 \(i \bmod 10=x\),即 \(i\) 的搭档数首位与 \(x\) 相等。

这个情况就比较复杂了。@CQBZJJH 奆佬用了很巧妙的方法推出了简洁的式子,但是我太蒟蒻了,不会那些花里胡哨的东西,所以就有了一个朴素的第二层循环 qwq。

我的想法就是这样的:既然你这个数无法确定位数为 \(k\) 的时候到底是否大于 \(n\),那么你就一点一点枚举呗!定义第二层循环 \(j\) 为中间的数字(\(j÷10\) 一定是一个 \(k-2\) 位数,位数不够前面补 \(0\)),其中 \(j\) 一定是 \(10\) 的倍数(因为要保留最后一位,从倒数第二位开始改),每次枚举时这个 \(i\) 的搭档数就是:

\(x\times 10^{k-1}+j+\operatorname{one}(i)\)

其中 \(\operatorname{one}(i)\) 是指求 \(i\) 的首位的函数(前面有)。

可以看出,只要这个数小于 \(n\),那循环就可以继续下去;但是如果这个数超出了 \(n\),因为 \(j\) 只会越来越大,不可能后面还有满足的,直接退出循环即可。

最后说一下 \(j\) 的枚举范围:\(0\sim 10^{k-1}-1\)(不能加到首位上去)。


一个小优化

适用于第三种方法,因为这个方法时间复杂度比较大,所以想到了这个。

试想一下:如果一个数的首位是相同的,那么它的数对的数量就相当于它末尾这个一位数的数对数量。所以,\(1\sim 9\) 可以与上面分开枚举,枚举 \(i\) 时把答案加在 \(b_i\) 里面,最后 \(ans←ans+b_i\) 即可。

注意一定要考虑和自己组成数对即一位数的情况,所以最后 \(b_i\) 需要 \(+1\)!!! 考试就栽在这个细节上了。)

其实想到了这个之后,就离想到上述的动态规划简单做法不远了……考试没想到有点可惜 qaq。

Code (第三种)

#include<bits/stdc++.h>
#define ll long long
using namespace std;
int n,k=1,x,a[10],b[10];
ll ans,t;
int one(int m){
while(m>=10) m/=10;
return m;
}
int buxian(int m){
int s=0;
for(int i=0;i<=m;i++) s+=pow(10,i);
return s;
}
int main(){
scanf("%d",&n);
if(n<10){
printf("%d",n);
return 0;
}
//求n的位数
a[1]=n;
x=one(n);
int u=n;
while(u){
u/=10;
a[++k]=u;
}
k--;
//求数
for(int i=1;i<=9;i++){
if(i<x) b[i]+=buxian(k-2);
else if(i==x){
b[i]+=buxian(k-3);
int y=pow(10,k-1);
for(int j=0;j<=y;j+=10){
if(x*y+j+one(i)<=n) b[i]++;
else continue;
}
}else b[i]+=buxian(k-3);
}
for(int i=1;i<=9;i++) b[i]++,ans+=b[i];
for(int i=11;i<=n;i++){
if(!(i%10)) continue;
t=i%10;
if(one(i)==t){
ans+=b[t];
continue;
}
if(t<x){
ans+=buxian(k-2);
continue;
}
if(t==x){
ans+=buxian(k-3);
int y=pow(10,k-1);
for(int j=0;j<=y;j+=10){
if(x*y+j+one(i)<=n) ans++;
else continue;
}
continue;
}
//t>x
ans+=buxian(k-3);
}
//for(int i=1;i<=9;i++) printf("%d ",b[i]);
printf("%lld\n",ans);
return 0;
}

时间复杂度的话……大概 \(Θ\big(9+\frac{1}{10}\times (n-9)^2+\frac{4}{5}\times (n-9)\big)\)??反正能过,极限数据大概 \(1.5\) 秒跑完。


写在最后

送给大家一句来自初三教练的名言:

你思维的深度决定你代码的长度。

这道题体现得淋漓尽致啊。

AT4828 [ABC152D] Handstand 2 TJ的更多相关文章

  1. TJ/T808 终端通讯协议设计与实现(码农本色)

    由于公司项目涉及到相关技术,对于平常写WEB的技术人员来说对这人来说比较默生:为了让下面的技术人员更好地对这个协议的实施,所以单独针对这个协议进行了分析和设计,以更于后期更好指导相关开发工作.由于自己 ...

  2. AtCoder Beginner Contest 124 D - Handstand(思维+前缀和)

    D - Handstand Time Limit: 2 sec / Memory Limit: 1024 MB Score : 400400 points Problem Statement NN p ...

  3. 半导体热阻问题深度解析(Tc,Ta,Tj,Pc)

    半导体热阻问题深度解析(Tc,Ta,Tj,Pc) 本文是将我以前的<有关热阻问题>的文章重新梳理,按更严密的逻辑来讲解. 晶体管(或半导体)的热阻与温度.功耗之间的关系为: Ta=Tj-* ...

  4. TJ Holowaychuk是怎样学习编程的?

    TJ Holowaychuk是怎样学习编程的? 学习了:https://blog.csdn.net/wozaixiaoximen/article/details/49507111 Q:TJ Holow ...

  5. 【算法】【网络流24题】巨坑待填(成功TJ,有时间再填)

    ------------------------------------------------------------------------------------ 17/24 --------- ...

  6. 【纪中集训】2019.08.10【NOIP提高组】模拟 A 组TJ

    T1 Description Solution 有待填坑-- T2 Description 给定一个\(h(≤10)\)层.\(n(≤10)\)行.\(m(≤10)\)列的由泥土组成的立方体,挖开\( ...

  7. CF877B Nikita and string TJ

    前言的前言 本 TJ 同步发布于洛谷,在线求赞(bushi 前言 蒟蒻第一篇题解,在线求审核大大给过 awa. 如果此题解有什么问题的话欢迎各位大巨佬提出. 题目链接:CF877B 题目类型:dp,一 ...

  8. tj

    --统计set @collSql='select sum(case Ca_IssueType when 0 then 1 else 0 end) as IssueCount,sum(case when ...

  9. SQL Server 2008 镜像的监控 - Joe.TJ -

    http://www.cnblogs.com/Joe-T/archive/2012/09/06/2673237.html

随机推荐

  1. Kubernetes自动横向伸缩集群节点以及介绍PDB资源

    在kubernetes中,有HPA在需要的时候创建更多的pod实例.但万一所有的节点都满了,放不下更多pod了,怎么办?显然这个问题并不局限于Autoscaler创建新pod实例的场景.即便是手动创建 ...

  2. SonarQube遇到的启动问题及解决方案

    操作系统:centos 7 (x86)一.问题描述:使用root启动时,一直反馈 SonarQube is not running问题原因:不能够使用root用户进行启动解决方案:①创建一个其他用户( ...

  3. 5、SpringBoot整合之SpringBoot整合MybatisPlus

    SpringBoot整合MybatisPlus 目录(可点击直接跳转,但还是建议按照顺序观看,四部分具有一定的关联性): 实现基础的增删改查 实现自动填充功能 实现逻辑删除 实现分页 首先给出四部分完 ...

  4. Android系统编程入门系列之界面Activity绘制展示

    上篇文章介绍了界面Activity的启动方式和生命周期,本篇将继续介绍在界面Activity中的内容是如何绘制展示给用户的. 在Android系统上运行新创建的界面Activtiy,给用户展示的是空白 ...

  5. Linux(CentOS)下安装docker

    Linux(CentOS)安装Docker 查看当前内核版本 [docker@localhost ~]$ uname -r 确保yum包更新到最新 [docker@localhost ~]$ sudo ...

  6. webpack(8)vue组件化开发的演变过程

    前言 真实项目开发过程中,我们都是使用组件化的去开发vue的项目,但是组件化的思想又是如何来的呢?下面就从开始讲解演变过程 演变过程1.0 一般情况下vue都是单页面开发,所以项目中只会有一个inde ...

  7. Extjs中由于ID重复引起的各种异常的解决方法

    很多人使用EXTJS中的Tabpanel遇到一个问题: 那就是在点击Tabpanel后,有时会发现Tabpanel出现错误,或无法Destroy已经关闭的panel,发现已经关闭的panel 中的组件 ...

  8. centos7 php开发环境安装-composer

    1.安装composer cd /usr/local/src curl -sS https://getcomposer.org/installer | php mv composer.phar /us ...

  9. 2012年第三届蓝桥杯C/C++程序设计本科B组省赛题目 海盗比酒量 结果填空

    ** 一.题目 ** 海盗比酒量 有一群海盗(不多于20人),在船上比拼酒量.过程如下:打开一瓶酒,所有在场的人平分喝下,有几个人倒下了.再打开一瓶酒平分,又有倒下的,再次重复- 直到开了第4瓶酒,坐 ...

  10. buu 相册

    一.拖入jeb,这个神器里面,感觉对jeb使用还是不熟悉,对我逆向产生了一些障碍. 抓住题目给的提示,邮箱,全局直接搜索,mail. 看下它的交叉引用 找到了发邮件的方法, C2的MAILFROME说 ...