2021牛客寒假算法基础集训营2
比赛链接
2021牛客寒假算法基础集训营2
I.牛牛的“质因数”
题目描述
算数基本定理,又称唯一分解定理,算术基本定理可表述为:任何一个大于1的自然数 \(\mathrm{N}\),如果 \(\mathrm{N}\) 不为质数,那么 \(\mathrm{N}\) 可 以唯一分解成有限个质数的乘积。即 \(N=p_{1}^{e 1} \cdot p_{2}^{e 2} \ldots p_{m}^{e m}\left(p_{1}<p_{2}<\ldots p_{m}\right)\)
朴素的质因子分解算法就是利用了算数基本定理,依次枚举p判断 \(N\) 是否包含素因子 \(p_{\text {。 }}\)
牛牛最近对于质因数分解产生了浓厚的兴趣。
牛牛定义了一个函数 \(F(x)\) ,它表示将x做质因数分解后得到的数字从小到大升序排列,然后将其 “拼接"成一个大整数。
例如 \(1500=2^{*} 2^{\star} 3^{\star} 5^{\star} 5^{\star} 5, F(1500)=223555\) 。
牛牛现在想要知道 \(\sum_{i=2}^{n} F(i)\) 的值。
由于这个结果非常大,所以你只用告诉牛牛最终答案对 \(10^{9}+7\) 取余数的结果即可。
输入描述:
仅一行一个正整数 \(n\left(2 \leq n \leq 4 \times 10^{6}\right)\)
输出描述:
仅一行, 表示答案对 \(10^{9}+7\) 取余数的结果。
示例1
输入
3
输出
5
示例2
输入
10
输出
342
说明
F(2)=2
F(3)=3
F(4)=22
F(5)=5
F(6)=23
F(7)=7
F(8)=222
F(9)=33
F(10)=25
2+3+22+5+23+7+222+33+25=342
解题思路
筛质数
对于一个数 \(X\),\(F[X]=x\times 10^{cnt[F[X/x]]}+F[X/x]\),其中 \(cnt\) 计算一个数的位数,\(x\) 为 \(X\) 的最小质因数 \(v[X]\),可用线性筛 \(O(n)\) 求出,另外,\(F[i]\) 的位数过大,不能直接计算,可以预处理出 \(F[X]\) 的位数,即 \(c[i]\) 为 \(F[i]\) 的位数,则:\(c[i]=cnt[v[i]]+c[i/v[i]]\),然后再预处理 \(10,100,1000,\dots\) 的取余即可
- 时间复杂度:\(O(n)\)
代码
// Problem: 牛牛的“质因数”
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/30771/I
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
// #define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=4e6+5,mod=1e9+7;
int n,m,prime[N],v[N],a[N],b[N],c[N],d[N];
int p[]={1,10,100,1000,10000,100000,1000000};
void primes(int n)
{
for(int i=2;i<=n;i++)
{
if(v[i]==0)
{
v[i]=i;
prime[++m]=i;
}
for(int j=1;j<=m;j++)
{
if(i>n/prime[j]||v[i]<prime[j])break;
v[i*prime[j]]=prime[j];
}
}
}
int main()
{
cin>>n;
primes(n);
for(int i=1;i<N;i++)a[i]=a[i/10]+1;
LL res=0;
d[0]=1;
for(int i=1;i<N;i++)d[i]=(1ll*d[i-1]*10)%mod;
for(int i=2;i<=n;i++)
{
c[i]=a[v[i]]+c[i/v[i]];
b[i]=(1ll*v[i]*d[c[i/v[i]]]+1ll*b[i/v[i]])%mod;
res+=b[i];
}
cout<<res%mod;
return 0;
}
J.牛牛想要成为hacker
题目描述
在算法竞赛中"hack"一般指用一组测试数据触发程序的缺陷,从而导致本来通过题目的AC代码无法通过该测试数据。
一般情况见得比较多的是用hack数据导致别人WA掉,当然也有一些会导致原本的AC代码TLE和MLE。
牛牛在一些简单的练习题时遇到了这样一个问题。
给定一个大小为n的数组\(a(1 \leq a_i \leq 10^9)\),然后请你判断数组元素是否能够从中选出三个组成一个三角形。
牛牛发现AC通过的代码中有这样一种暴力逻辑,该逻辑的伪代码如下。
FOR i = 1 ... n
FOR j = i + 1 ... n
FOR k = j + 1 ... n
IF isTriangle(a[i],a[j],a[k])
print("yes")
EXIT
END IF
END FOR
END FOR
END FOR
print("no")
EXIT
其实就是三重循环枚举数组的三个元素,检查是否为三角形。这段代码很取巧的地方在于它存在一种“短路”逻辑,一旦发现存在三角形就立刻终止程序。
这样在随机数据下其实很容易发现三角形,所以如果数据纯随机,显然这就是一段AC代码。
牛牛当然知道这个代码很明显就存在缺陷,如果数据构造的好的话应该可以卡TLE,但是牛牛发现,他并不会构造出能够hack这个暴力算法的数据,所以他请你来帮他。
我们以这段程序调用isTriangle的次数作为时间复杂度的计算依据,请你构造数据hack这段暴力程序,使它TLE掉。
输入描述:
第一行输入一个正整数\(n(3 \leq n \leq 10^5)\)表示需要你构造的数组大小。
输出描述:
输出\(n\)个正整数,正整数的范围在\([1,10^9]\)之间,要求该暴力程序在运行过程中调用isTriangle函数的次数不得少于\(min(C_n^3,n^2\left \lfloor log_2n \right \rfloor)\)
示例1
输入
3
输出
2 2 2
说明
当n=3时题目要求小w的程序调用isTriangle函数的次数不得少于1次,所以输出任意的3个正整数都能符合条件。
示例2
输入
10
输出
1 2 4 8 16 32 64 128 256 512
说明
由于任何三个数字都无法组成三角形,所以会扫描到最后一组,达到最大复杂度,一共调用了120次isTriangle函数。
解题思路
构造
考虑使用斐波那契数列来构造:任意三个数不能形成三角形的最慢的数列,当某个数大于 \(1e9\) 时停止构造,后面全部补 \(1\),这样前面的斐波那契大约有 \(40\) 来个,当三个数都为 \(1\) 时才形成三角形,即至少要枚举 \(40n^2\) 次,满足题目要求
- 时间复杂度:\(O(n)\)
代码
// Problem: 牛牛想要成为hacker
// Contest: NowCoder
// URL: https://ac.nowcoder.com/acm/contest/30771/J
// Memory Limit: 524288 MB
// Time Limit: 2000 ms
//
// Powered by CP Editor (https://cpeditor.org)
// %%%Skyqwq
#include <bits/stdc++.h>
//#define int long long
#define help {cin.tie(NULL); cout.tie(NULL);}
#define pb push_back
#define fi first
#define se second
#define mkp make_pair
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
typedef pair<LL, LL> PLL;
template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; }
template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; }
template <typename T> void inline read(T &x) {
int f = 1; x = 0; char s = getchar();
while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); }
while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar();
x *= f;
}
const int N=1e5+5;
int a[N];
int n;
int main()
{
cin>>n;
a[0]=1;
a[1]=2;
int i=2;
while(a[i-1]+a[i-2]<=1e9)a[i]=a[i-1]+a[i-2],i++;
for(int j=i;j<N;j++)a[j]=1;
for(int i=1;i<=n;i++)cout<<a[i]<<' ';
return 0;
}