深入解析:河南萌新联赛2025第(五)场:信息工程大学”(补题)

前言

很无语的一场,说到底,还是自己太钻牛角尖。

比赛链接:河南萌新联赛2025第(五)场:信息工程大学”


A. 宇宙终极能量调和与多维时空稳定性验证下的基础算术可行性研究

直接靠猜,从0到2,试过去了。直接输出2就行

B. 中位数

通过手写一个例子,会发现,到最后就是最大与最小和的一半
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    ll a[N];
    void solve()
    {
    ll n;
    cin>>n;
    for(ll i=1;i<=n;i++)
    cin>>a[i];
    if(n<=2)
    {
    if(n==1)
    cout<<a[1]<<endl;
    else
    cout<<
    (a[1]+a[2])/2<<endl;
    return ;
    }
    sort(a+1,a+n+1);
    cout<<
    (a[1]+a[n])/2<<endl;
    }
    signed main()
    {
    IOS;
    ll t=1;
    // cin>>t;
    while(t--)
    solve();
    return 0;
    }

C. 中位数+1

通过题目,会发现是个对顶堆的模板,其实这道题比赛的时候看了,知道该怎么操作,但是忘了代码应该如何写了,直接放弃了,还是自己的实现能力不强。
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    void solve()
    {
    ll n;
    cin>>n;
    //例子 1,2,3,4,5
    priority_queue<ll,vector<ll>
      ,greater<ll>> mi;
        //存放从小到大的,堆顶为该堆的最小值{5,4}
        priority_queue<ll,vector<ll>
          ,less<ll>> ma;
            //存放从大到小的,堆顶为改堆的最大值,存{1,2,3}
            for(ll i=1;i<=n;i++)
            {
            ll a;
            cin>>a;
            if(ma.empty()||a<=ma.top())
            ma.push(a);
            else
            mi.push(a);
            if(ma.size()>mi.size()+1)
            {
            mi.push(ma.top());
            ma.pop();
            }
            else if(mi.size()>ma.size())
            {
            ma.push(mi.top());
            mi.pop();
            }
            if(i%2!=0)
            {
            cout<<ma.top()<<
            " ";
            }
            else
            {
            cout<<
            (ma.top()+mi.top())/2<<
            " ";
            }
            }
            }
            signed main()
            {
            IOS;
            ll t=1;
            // cin>>t;
            while(t--)
            solve();
            return 0;
            }

E. 中位数+3

这一题需要用到一个公式Legendre 公式
在这里插入图片描述
问题是:在 K 进制下求 n ! n! n!(n 的阶乘 )的后置零的个数。
然而后置零取决于该阶乘中包含多少个k的因子。
例如:
在十进制下,后置零的个数由 10 的因子个数决定,而 10 = 2 × 5 10 = 2 \times 5 10=2×5,所以实际上由 2 和 5 中较少的那个因子个数决定。
比如:如果 K = 12 K = 12 K=12,则分解为 2 2 × 3 1 2^2 \times 3^1 22×31
在这里插入图片描述

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    ll n,k;
    vector<pii> p;
      void yin()
      {
      // 从2开始遍历到sqrt(k),寻找k的素因数
      for(ll i=2;i*i<=k;i++)
      {
      // 如果i是k的因数
      if(k%i==0)
      {
      ll cnt=0;
      // 统计i作为素因数的指数
      while(k%i==0)
      {
      k/=i;
      cnt++;
      }
      // 将素因数i和其指数cnt存入vector p中
      p.push_back({i,cnt
      });
      }
      }
      // 如果k最后大于1,说明剩下的k本身是一个素数
      if(k>
      1)
      p.push_back({k,1
      });
      }
      void solve()
      {
      cin>>n>>k;
      // 对k进行素因数分解
      yin();
      // 初始化答案为一个很大的数,用于后续取最小值
      ll ans=1e18;
      // 遍历k的所有素因数及其指数
      for(auto i:p)
      {
      // a是当前素因数
      ll a=i.fi;
      // s是当前素因数在k中的指数
      ll s=i.se;
      // num用于统计n!中包含a的个数
      ll num=0;
      ll c=n;
      // 使用Legendre公式计算n!中a的个数
      while(c)
      {
      num+=c/a;
      c/=a;
      }
      // 计算当前素因数能组成多少个k,取最小值作为可能的答案
      ans=min(ans,num/s);
      }
      // 输出最终结果,即k进制下n!的后置零个数
      cout<<ans<<endl;
      }
      signed main()
      {
      IOS;
      ll t=1;
      // cin>>t;
      while(t--)
      solve();
      return 0;
      }

F. 中位数+4

同样是让求后置零,但是在10进制下,只需要循环判断就行了
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    void solve()
    {
    ll n,k;
    cin>>n>>k;
    ll ans=0;
    while(n%k==0)
    {
    n=n/k;
    ans++;
    }
    cout<<ans<<endl;
    }
    signed main()
    {
    IOS;
    ll t=1;
    // cin>>t;
    while(t--)
    solve();
    return 0;
    }

G. 简单题

看到这一题,以为是数相加,然后找规律,结果啊,wa16次,虽然其中有过怀疑是行列式,二阶还好,还会,到了三阶行列,不会了,想着这才大一,怎么会出这方面的知识,然后直接舍弃掉这方面的想法。继续埋头计算和的规律,呵呵,到头来小丑一个。
说到底还是数学没学好,记得大一的时候老师当时讲过三阶行列式怎么算的,但是,给忘了。
通过拆分会发现是斐波那契数列,然后找规律就行
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    void solve()
    {
    ll n;
    cin>>n;
    if(n%3==1)
    cout<<
    1<<endl;
    else
    cout<<
    0<<endl;
    }
    signed main()
    {
    IOS;
    ll t=1;
    // cin>>t;
    while(t--)
    solve();
    return 0;
    }

H. 简单题+

这一题是上一题的延伸,只不过求模的数变的很大
需要用到斐波那契数列的求和规律
F(n)=f(n+2)-1;
然后还要利用到矩阵加速,这避免不了矩阵方面的知识
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    #define vc vector<vector<ll>>
      const ll N=1e6+10;
      const ll mod=998244353;
      // 斐波那契递推矩阵:[[1,1],[1,0]],用于矩阵乘法
      vc a={
      {
      1,1
      },{
      1,0
      }
      };
      vc jz(vc &b, vc &c)
      {
      // 初始化结果矩阵(2x2),初始值为0
      vc ans(2, vector<ll>(2, 0));
        // 矩阵乘法核心:i行×k列 乘以 k行×j列 → i行×j列
        for(ll i=0;i<
        2;i++) // 结果矩阵的行
        for(ll j=0;j<
        2;j++) // 结果矩阵的列
        for(ll k=0;k<
        2;k++) // 中间维度(矩阵b的列,矩阵c的行)
        ans[i][j] = (ans[i][j] + b[i][k] * c[k][j] % mod) % mod;
        return ans;
        }
        vc f(ll p)
        {
        // 初始化单位矩阵(乘法单位元,类似数字1)
        vc ans={
        {
        1,0
        },{
        0,1
        }
        };
        // 单位矩阵:任何矩阵乘以它都等于自身
        while(p >
        0) // 快速幂核心:分解指数p为二进制
        {
        if(p &
        1) // 如果当前二进制位为1,将结果乘以当前矩阵a
        ans = jz(ans, a);
        p >>= 1;
        // 指数右移一位(相当于除以2)
        a = jz(a, a);
        // 矩阵a自乘,即a^2, a^4, a^8...(对应二进制位的权重)
        }
        return ans;
        }
        void solve()
        {
        ll n;
        cin >> n;
        // 计算矩阵a的(n+1)次幂,通过矩阵快速幂得到斐波那契相关结果
        vc ans = f(n + 1);
        // 结果为矩阵[1][1]位置的值减1,取模后输出
        // 原理:斐波那契数列求和公式与矩阵幂的关系,F(0)+F(1)+...+F(n) = F(n+2) - 1
        cout <<
        (ans[1][1] - 1 + mod) % mod << endl;
        // +mod避免负数取模
        }
        int main()
        {
        IOS;
        ll t = 1;
        // cin >> t; 
        while(t--)
        solve();
        return 0;
        }

I. 从零开始的近世代数复习(easy)

由于是简单版本,k为2,由此问题退化到了最近公共祖先的路径和
并且注意这个条件:
在这里插入图片描述
说明根节点1,是父节点,即是前置定理,所以求出lca不是根节点的话,还要再与根节点求一次lca。当然如果用朴素法的话,肯定会超时,为此需要倍增加速
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    ll g[N][30];
    ll f[N][30];
    ll d[N];
    vector<ll> ed[N];
      ll ans=0;
      void dfs(ll x,ll fa)//打ST表
      {
      f[x][0]=fa;
      d[x]=d[fa]+1;
      for(ll i=1;i<
      30;i++)
      {
      f[x][i]=f[f[x][i-1]][i-1];
      g[x][i]=g[f[x][i-1]][i-1]+g[x][i-1];
      }
      for(auto i:ed[x])
      if(i!=fa)
      dfs(i,x);
      }
      ll lca(ll x,ll y)//求lca
      {
      if(d[x]<d[y])swap(x,y);
      for(ll i=29;i>=0;i--)
      {
      if(d[f[x][i]]>=d[y])
      {
      ans+=g[x][i];
      x=f[x][i];
      }
      }
      if(x==y)
      return x;
      for(ll i=29;i>=0;i--)
      {
      if(f[x][i]!=f[y][i])
      {
      //ans+=g[x][i]+g[y][i];
      x=f[x][i];
      y=f[y][i];
      }
      }
      ans+=g[x][0]+g[y][0];
      return f[x][0];
      }
      void solve()
      {
      ll n;
      cin>>n;
      for(ll i=1;i<=n;i++)
      cin>>g[i][0];
      for(ll i=1;i<=n-1;i++)
      {
      ll u,v;
      cin>>u>>v;
      ed[u].push_back(v);
      }
      dfs(1,0);
      ll q;
      cin>>q;
      while(q--)
      {
      ll k;
      cin>>k;
      ll x,y;
      cin>>x>>y;
      ans=g[1][0];
      ll s=lca(x,y);
      if(s!=1)//如果不是根节点,要接着找
      lca(s,1);
      cout<<ans<<endl;
      }
      }
      signed main()
      {
      IOS;
      ll t=1;
      //cin>>t;
      while(t--)
      solve();
      return 0;
      }

K. 狂飙追击

这一题可以用BFS过,当然这是因为数据不够精细,正解是逆序模拟,由于正序有太多的选择了,
然而逆序的优势在于:
每一步的前序状态是唯一的,可以确定性地倒推。
例如:若当前点是 (a, b) 且 a > b,那么它只能由 (a - b, b) 移动而来(因为正向移动时只有 max(x,y)=b 才能让 x 增加 b 到达 a)。
若 tx >= ty(通过交换保证此条件统一处理)
此时 tx 是较大值,根据正向移动规则,tx 只能是由 ty 扩展而来:

若 tx > 2ty:说明最近多次移动都是沿 x 轴(每次增加 ty),可以一次性倒推多步(tx = tx / 2,前提是 tx 为偶数,否则无法整除,无解)。
若 tx ≤ 2
ty:只能倒推一步(tx = tx - ty),因为再往前倒推会导致 tx < ty,破坏当前大小关系。
代码如下:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    void solve()
    {
    ll a,b,c,d;
    cin>>a>>b>>c>>d;
    ll ans=0;
    while(1)
    {
    if(a>c||b>d)//判断是否无解
    {
    cout<<
    -1<<endl;
    return ;
    }
    if(a==c&&b==d)//满足条件直接输出
    {
    cout<<ans<<endl;
    return ;
    }
    if(c<d)//保持终点x最大,这样减少判断情况
    swap(c,d),swap(a,b);
    if(c>=2*d)//只有这种情况下,x除以2,会使步数最小化
    {
    if(c%2!=0)//如果为奇数,则一定不成立,因为最后一步都是由小的传过来,咋样看都是二倍关系
    {
    cout<<
    -1<<endl;
    return ;
    }
    c/=2;
    }
    else c-=d;
    //如果小于的话,则说明,是加上y的
    ans++;
    }
    }
    signed main()
    {
    IOS;
    ll t=1;
    // cin>>t;
    while(t--)
    solve();
    return 0;
    }

L. 防k题

看完题后,很快就会想到二分,但是唯一比较难搞的是check函数的模拟。
代码:

#include<bits/stdc++.h>
  using namespace std;
  #define IOS ios::sync_with_stdio(0),cin.tie(0),cout.tie(0)
  #define ll long long
  #define endl '\n'
  #define pii pair<ll,ll>
    #define fi first
    #define se second
    const ll N=1e6+10;
    ll x1,c,z,x2,y2;
    bool check(ll x)
    {
    ll cc=x1;
    //咔咔的血量
    ll g=c;
    //初始攻击
    ll sum=x2;
    //战士的血量
    while(1)
    {
    //注意一定要小于等于,因为下面的循环模拟,可能会使x<0.
    if(x<=0)//如果咔咔没了,就说明不成立
    return false;
    if(sum<=0)//如果战士死亡,说明成立
    return true;
    for(ll i=1;i<=3;i++)//模拟三次最优攻击
    {
    cc-=y2;
    //每次咔咔承受一次攻击,并扣除血量
    if(cc<=0)//如果一只咔咔阵亡,则战士换另一只攻击
    x--,cc=x1;
    //总咔咔数量减1
    }
    sum-=g*x;
    //战士承受的伤害等于存活咔咔的总攻击
    g+=z;
    //每回合咔咔的攻击力增加z
    }
    }
    void solve()
    {
    cin>>x1>>c>>z>>x2>>y2;
    ll l=1,r=1e10;
    while(l<r)
    {
    ll mid=l+((r-l)>>
    1);
    if(check(mid))
    {
    r=mid;
    }
    else
    l=mid+1;
    }
    cout<<r<<endl;
    }
    signed main()
    {
    IOS;
    ll t=1;
    // cin>>t;
    while(t--)
    solve();
    return 0;
    }

总结·

这场比赛,突出了一个问题,就是自己太容易陷入一个方向出不来,还有就是模板太容易忘记了,之前接雨水那一道题的模板也是忘了,差点没写出来,而这次是真的没写出来~~~~~,还有就是比较怕搜索题,一般看到那种题,就没写的欲望,就想着其他方法,这回依旧想其他方法,一如既往,没想出来。看到前面写的,原来自己这么多短板,尽力改掉吧

posted @ 2025-08-15 20:19  wzzkaifa  阅读(7)  评论(0)    收藏  举报