【题解】关于新生赛部分题目的

博客分享了数学竞赛中的两个问题:一是如何构造等边三角形,通过分析得出利用map存储虚拟直线的解决方案;二是探讨了找一个数的约数个数的方法,强调理解质因数分解和排列组合的重要性。博客作者通过实例解析了思路,并提供了AC代码。
摘要由CSDN通过智能技术生成

问题 L: 寄蒜几盒?

题目传送门

此题很难吗?貌似是,反正当时没人A掉,或许是因为大佬们不主动带路,我们没怎么敢看这道题,赛后看题,发现emm…这题还可以(逃)

题目描述见传送门

我们可以提炼出几个要点:

题目是个圆,弱弱的问一句有存圆的数据结构或类型嘛?呃,看成一条直线不就行。
题目数据量感人,我能不能O(N^3)解决?emm....

步入正题:

我的思路:
1.首先既然能构成等边三角形,那么必然,三个点是要平分整个圆的。也就是说只要存在一组三个点他们之间的距离都是三分之一圆弧就行,也就是周长的三分之一。
2.把一个圆找个位置切开,然后拉成一条直线,然后把题目给我们的每一个圆上的点都在这条虚拟的直线上标记一下顺便记录每个点的坐标,那么如果我们能找到三个点坐标之差都为三分之一圆弧就好了。
3.问题在于怎么把虚拟的直线给存下来(用map)

在这里插入图片描述

在这里插入图片描述

为什么用map,一般标记东西不是直接用数组嘛?因为这里直线可能过于长,数组会直接爆炸,(现场的时候我就没想到用map,赛完康哥给我提示一下,我恍然大雾,你说你真是的,为啥不在现场赛的时候给我个眼神提示我一下呢?差评!哈哈emm…)我们这里开一个map记录一下直线上的哪个坐标被标记了 这样操作map< long long bool> m;
这里贴上AC代码,速度一般,还有很大的优化潜力。没朋哥快,但是比林姐快多了(逃…)
#include<iostream>
#include<map>
using namespace std;
map<long long,int>m;
int n;
long long ll;  
long long p;
long long a;
long long x[1000006];
int main()
{
 cin>>n;
 for(int i=1;i<=n;i++)
 {
  scanf("%lld",&a);
  m[ll] = 1;    // 这里在那条长线上标记一下这个点是存在的
  x[i] = ll;    // x[i]记录第i个点的坐标
  ll += a;    // ll记录当前点的坐标,加到最后也便成了周长
 }
 if(ll % 3 != 0)
 {
  cout<<"No";    // 这里这个特判最后说
 }
 else
 {
  p = ll / 3;    //半径找一下
  int ok = 0;    
  for(int i = 1;i <= n; i++)  // 枚举每个点
  {
   long long x1 = x[i] + p;
   long long x2 = x[i] + p + p;
   if(x1 >= ll || x2 >ll)  //如果大于线的长度就跳出就好了,因为这两个点必定在前边已经检查过了
   break;
   if(m[x1] && m[x2])  
   {
    ok = 1;
    break;     // 如果能找到一组就尽退出即可
   }
  }
  if(ok)
  cout<<"Yes";
  else cout<<"No";
 }
 return 0;
}

  
emm,这缩进有问题…
提示1:关于周长不能被3整除的特判,朋哥给最先发现的,然鹅OJ数据并没有类似的数据,加不加都给过了,但最好加上,想一想为什么?
提示2:如果一个点它加上两倍三分之一弧长或者加上一倍三分之一弧长已经超出圆的周长的话,那么不用判断因为他必定已经在前边给判断了。举个栗子:
假设三分之一弧长为3,周长为9,我们依次判断每个点
从坐标为1的点开始,(1,1+3,1+6);(2,2+3,2+6);(3,3+3,3+6);(4,4+3,4+6)到这里你会发现4+6已经超过周长9,
但是你想一下超过周长的我们必定要从头开始就会变成(4,4+3,4+6-9)也就是(4,4+3,1)换换数字位置也就是(1,4,7)看之前已经判断过(1,1+3,1+6)了也就是(1,4,7)
本题我跑出50毫秒,其实可用二分搜索,速度又有质的提高(朋哥10毫秒)另外还有林姐故意测试OJ承受能力的1500毫秒的神奇解法,大家可以多多摸索

问题 A: 约数个数

题目传送门

此题应该就是A10题与A11题拉开差距的关键一题,看起来像是高深的数论?(原谅我大脑里数论几乎是一片空白)

其实你会发现一定的性质,我是说赛场临时发现而之前没有相应知识储备的情况,所以这里我不给出标准的关于题目的证明,我只给出怎么样在赛场上想此题

首先明确一点m的约数不是从1到m枚举一遍!!!

另外一个质数的倍数肯定能被质数整除吧,貌似所有的数都是这样

假设M=18 则 18 = 2 ^ 1 * 3 ^ 2,18肯定不会被5这个质数整除。为什么?因为5本身是个质数,他不可能被除他本身和1之外的数整除,而18所分解开的质数的乘积中没有5所以说18的约数中没有5以及5的倍数

那么18能被2整除嘛?答案是肯定的因为18分解的质数的乘积中有2的存在

理解了以上我所描述的“不清晰”的描述,你就几乎能解决这道题了

还有的一点就是我知道了能不能整除,但问题是要求约数中能被整除的数的个数啊,这简单,(排列组合,乘法原理!!!)

举个栗子,还是18,我前边以及说了质数的整数倍肯定能被他本身整除,ok,那18 = 2 ^ 1 * 3 ^ 2 关于2的倍数你能找到几种组合?无非就是2 ^ 1 * 3 ^ 0 = 2 ; 2 ^ 1 * 3 ^ 1 = 6 ; 2 ^ 1 * 3 ^ 2 =18;只有这三种组合 也便是答案了

关于样例 你也可以动手试试,然后知道这个规律我们再解题可谓迎刃而解了

AC代码

#include<iostream>
using namespace std;
int a[1000];            //这个数组记录质数
int b[1000];            //这个数组记录质数的指数
int  main()
{
    int ok=0;
    long long num=1;
    int n,g;
    cin>>n>>g;
    for(int i=1;i<=n;i++)
    {
        cin>>a[i];
        if(a[i]==g)     //我们在m的质数的分解中找到质数g
        ok=i;
    }
    for(int i=1;i<=n;i++)
    {
        cin>>b[i];
        if(i!=ok)
        num*=(b[i]+1);      //乘法原理,计算最后有多少种组合,这里我没给出解释,自己思考一下
    }
    if(g==1)
    {
        long long s=1;
        for(int i=1;i<=n;i++)
        s*=b[i];
        cout<<s;
    }
    else
    {
        if(ok==0)   // 如果当时众多质数中根本不存在此质数,直接输出0
        cout<<0;
        else cout<<b[ok]*num;   //如果存在,输出多少种组合
    }
    return 0;
}

很多同僚其实不是没有能力解这道题,只是觉得当时A此题的人比较少,又看看差不多数论的样子,几乎没怎么思考就放弃了,结果我没放弃最后提交,揭榜后几个被我爆菊的哥们一脸懵逼,(抱歉各位)还好这是校赛。

希望我们继续努力,以后有有机会不管是什么比赛都要坚持坚持再坚持,坚持到最后一秒,我们不要被其他学校爆菊。加油!!!

本次成文也较为仓促,如有漏误还请多包涵,能够私聊我提醒我改正最好,谢谢各位了…

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值