2022牛客寒假算法基础集训营

本文解析了两道算法竞赛题目,一是通过数字根求方案数的问题,利用DP算法进行解答;二是关于欧拉函数及质数的数论题,探讨了如何找到特定区间内H(x)=φ(x)/x的最大最小值。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

A 九小时九个人九扇门

题目链接

题目大意

给你n个数,你可以选择任意个数相加求数字根(将该数字各数位上的数字相加得到一个新的数,直到得到的数字小于10为止)。问你最后组成1~9的方案数。

解法/思路

首先一个数的数字根就是对这个数取余9的结果(结果为0时,数字根为9).
证明:一个数的数字根就是这个数取余9

设数x=abcd(a,b,c,d分别为千,百,十,个位上的数)
则	x=a*1000+b*100+c*10+d
		x%9=(a*1000+b*100+c*10+d)%9
		x%9=a%9+b%9+c%9+d%9
		x%9=(a+b+c+d)%9
又因为a+b+c+d就是数x的数字根。
则说明取余9不影响数的数字根
例如14和5同余9.就说明14和5的数字根相同。又因为小于10的数的数字根为本身
则一个数的数字根即为这个数对9取余(结果为0的时候,数字根为9).

使用的算法:简单dp,类似于01背包,求方案数。
定义状态:f(i,j)代表前i个数组成数字根为j的方案数
状态转移方程:

f(i,(j+a[i]))=f(i-1,j)+f(i-1,(j+a[i])%9

代码

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
const int p=998244353;
int a[N];
int dp[N][20];//dp[i][j]表示前i个数根为j的个数
signed main()
{
    int n;
    scanf("%lld",&n);
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        a[i]%=9;
    }
    for(int i=1;i<=n;i++)
    {
        //f(i,(j+a[i]))=f(i-1,j)+f(i-1,(j+a[i])%9
        for(int j=0;j<=9;j++)
        {
            dp[i][(j+a[i])%9]=(dp[i-1][j%9]+dp[i-1][(j+a[i])%9])%p;
        }
        dp[i][a[i]]++;
    }
    for(int i=1;i<=9;i++)
    {
        cout<<dp[n][i==9? 0:i]<<" ";
    }
    return 0;
}

B 炸鸡块君与FIFA22

还不会…

C Baby’s first attempt on CPU

还没补题…

D 牛牛做数论

题目链接

题目大意

定义H(x)=φ(x)/x
其中φ(x)就是欧拉函数
给你一个数n,让你求
1.2~n中H(x)取最小值的时候,x的值(如果有多个取最小的x)
2.2~n中H(x)取最大值的时候,x的值(如果有多个取最大的x)

解法/思路

首先需要知道的是欧拉函数就是对于一个正整数n,小于n且和n互质的正整数(包括1)的个数,记作φ(n).
欧拉函数的通式:φ(n)=n*(1-1/p1)(1-1/p2)(1-1/p3)*(1-1/p4)……(1-1/pn)
其中p1, p2……pn为n的所有质因数,n是不为0的整数。φ(1)=1(唯一和1互质的数就是1本身)。
欧拉函数

则H(n)=(1-1/p1)(1-1/p2)(1-1/p3)*(1-1/p4)……(1-1/pn)
H(n)=((p1-1)/p1)((p2-1)/p2).....
其中p1, p2……pn为n的所有质因数,n是不为0的整数。

因为H(2)=1/2 H(3)=2/3 H(5)=4/5…H(x)=(x-1)/x 这里的x为质数
所以x是越大的质数则说明H(x)的值越大。
可以很容易发现H(x)的最大值就是x取小于等于n的最大质数。
证明:

假设H(x)的最大值不是x取小于等于n的最大质数p
则当p<x<=n时,因为最大的质数为p,则x一定是个合数,可以分解成至少两个质数相乘
由展开式可以得到其中一项必定小于等于H(p)
又因为每一项都小于1,则乘完之后必定小于H(p)
当x<p时,显然H(x)<H(p)....这个自己应该都能看出来吧,就不用证明了吧
则假设不成立。
说明H(x)的最大值就是x取小于等于n的最大质数p

结论
最小的值就是前面尽可能多的质数相乘
最大的值就是小于等于n的最大质数

#include <bits/stdc++.h>
using namespace std;
#define int long long
const int N=1e5+10;
int a[30]={0,2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73};
int x[30];
void init()
{
    int s=1,n=20;
    for(int i=1;i<=15;i++)
    {
        s*=a[i];
        x[i]=s;
    }
}
bool prime(int x)
{
    for(int i=2;i*i<=x;i++)
    {
        if(x%i==0) return 0;
    }
    return 1;
}
signed main()
{
    init();
    int T;
    cin>>T;
    while(T--)
    {
        int n;
        cin>>n;
        if(n==1) {cout<<-1<<endl;continue;}
        for(int i=15;i>=1;i--)
        {
            if(x[i]<=n)
            {
                cout<<x[i]<<" ";
                break;
            }
        }
        for(int i=n;i>=1;i--)
        {
            if(prime(i))
            {
                cout<<i<<endl;
                break;
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值