河南萌新联赛2025第(五)场:信息工程大学”(补题)
文章目录
- 前言
- A. 宇宙终极能量调和与多维时空稳定性验证下的基础算术可行性研究
- B. 中位数
- C. 中位数+1
- E. 中位数+3
- F. 中位数+4
- G. 简单题
- H. 简单题+
- I. 从零开始的近世代数复习(easy)
- K. 狂飙追击
- L. 防k题
- 总结·
前言
很无语的一场,说到底,还是自己太钻牛角尖。
比赛链接:河南萌新联赛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;elsecout<<(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,5priority_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);elsemi.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×510 = 2 \times 510=2×5,所以实际上由 2 和 5 中较少的那个因子个数决定。
比如:如果 K=12K = 12K=12,则分解为 22×312^2 \times 3^122×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;elsecout<<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),初始值为0vc 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,将结果乘以当前矩阵aans = 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) - 1cout << (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 ≤ 2ty:只能倒推一步(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;
}
总结·
这场比赛,突出了一个问题,就是自己太容易陷入一个方向出不来,还有就是模板太容易忘记了,之前接雨水那一道题的模板也是忘了,差点没写出来,而这次是真的没写出来~~~~~,还有就是比较怕搜索题,一般看到那种题,就没写的欲望,就想着其他方法,这回依旧想其他方法,一如既往,没想出来。看到前面写的,原来自己这么多短板,尽力改掉吧