文章目录
- 前言
- [A. 宇宙终极能量调和与多维时空稳定性验证下的基础算术可行性研究](#A. 宇宙终极能量调和与多维时空稳定性验证下的基础算术可行性研究)
- [B. 中位数](#B. 中位数)
- [C. 中位数+1](#C. 中位数+1)
- [E. 中位数+3](#E. 中位数+3)
- [F. 中位数+4](#F. 中位数+4)
- [G. 简单题](#G. 简单题)
- [H. 简单题+](#H. 简单题+)
- [I. 从零开始的近世代数复习(easy)](#I. 从零开始的近世代数复习(easy))
- [K. 狂飙追击](#K. 狂飙追击)
- [L. 防k题](#L. 防k题)
- 总结·
前言
很无语的一场,说到底,还是自己太钻牛角尖。
A. 宇宙终极能量调和与多维时空稳定性验证下的基础算术可行性研究
直接靠猜,从0到2,试过去了。直接输出2就行
B. 中位数
通过手写一个例子,会发现,到最后就是最大与最小和的一半
代码:
cpp
#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
通过题目,会发现是个对顶堆的模板,其实这道题比赛的时候看了,知道该怎么操作,但是忘了代码应该如何写了,直接放弃了,还是自己的实现能力不强。
代码:
cpp
#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。
cpp
#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进制下,只需要循环判断就行了
代码:
cpp
#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次,虽然其中有过怀疑是行列式,二阶还好,还会,到了三阶行列,不会了,想着这才大一,怎么会出这方面的知识,然后直接舍弃掉这方面的想法。继续埋头计算和的规律,呵呵,到头来小丑一个。
说到底还是数学没学好,记得大一的时候老师当时讲过三阶行列式怎么算的,但是,给忘了。
通过拆分会发现是斐波那契数列,然后找规律就行
代码:
cpp
#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;
然后还要利用到矩阵加速,这避免不了矩阵方面的知识
代码:
cpp
#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。当然如果用朴素法的话,肯定会超时,为此需要倍增加速
代码:
cpp
#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,破坏当前大小关系。
代码如下:
cpp
#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函数的模拟。
代码:
cpp
#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;
}
总结·
这场比赛,突出了一个问题,就是自己太容易陷入一个方向出不来,还有就是模板太容易忘记了,之前接雨水那一道题的模板也是忘了,差点没写出来,而这次是真的没写出来~~~~~,还有就是比较怕搜索题,一般看到那种题,就没写的欲望,就想着其他方法,这回依旧想其他方法,一如既往,没想出来。看到前面写的,原来自己这么多短板,尽力改掉吧