当前位置: 首页 > news >正文

河南萌新联赛2025第(二)场:河南农业大学

我看到花儿在绽放 我听到鸟儿在歌唱 我看到人们匆匆忙忙
我看到云朵在天上 我听到小河在流淌 我看到人们漫步在路上

河南萌新联赛2025第(二)场:河南农业大学

河南萌新联赛2025第(二)场:河南农业大学_ACM/NOI/CSP/CCPC/ICPC算法编程高难度练习赛_牛客竞赛OJ

第二场,暴露了很多问题,这次的题目数学数论题居多,有点样例都看不明白,做的时候都快崩溃了。数学不好太难了;数论还没咋看,导致一些简单的题推不出来;有一些模板题没见过很不好想,但是想明白后不算难;

预估难度:

  • 简单:K I M D
  • 中等:E C B A L
  • 较难:H G
  • 困难:J F

我的做题顺序 K I M D A,数学太难了XD;虽然最后榜又歪了但是这次影响不大,自己做题的顺序也是按难度来的;


目录

  • 河南萌新联赛2025第(二)场:河南农业大学
    • K-打瓦
    • I-猜数游戏(easy)
    • M-米娅逃离断头台
    • D-开罗尔网络的备用连接方案
    • E-咕咕嘎嘎!!!(easy)
    • C-O神
    • B-异或期望的秘密
      • 小蓝的二进制询问
      • 累加器
      • 异或期望的秘密
    • A-约数个数和
      • 整除分块

K-打瓦

签到题,使出PHP大法;

gugugaga

PHP 是一种创建动态交互性站点的强有力的服务器端脚本语言。意思就是说php语言会把不是代码的东西直接输出;


I-猜数游戏(easy)

签到题;最优策略肯定是二分的思路去猜,所以需要log2(n)次,向上取整;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
void slove(){int n;cin>>n;int an=(int)log2(n);if(log2(n)>an) an++;cout<<an;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

M-米娅逃离断头台

数学题;设大圆半径为aaa小圆半径为bbb从圆心连向切线要求的面积为π×(a2−b2)/2\pi\times(a^2-b^2)/2π×(a2b2)/2,又勾股定理可得a2−b2=x2/4a^2-b^2=x^2/4a2b2=x2/4,固答案为π×x2/8\pi\times x^2/8π×x2/8

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
const double pi=3.1415926535;
void slove(){int x;cin>>x;double c=x/2.0;double an=0.5*pi*c*c;printf("%.2lf",an);
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

D-开罗尔网络的备用连接方案

一道很简单的遍历树的题,先按要求建树,然后dfs遍历统计到达每个节点是当前值的二进制数里一的个数,最后多次寻问含有相应个数的1的节点有几个,对应查询就好;

输入边的时候顺序不是固定的,所以建树的时候记得建双向边!这样才能把图连起来;便利的时候打标记或记录父节点,防止走回头路;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
vector<int> e[N];
int a[N];
int an[N];
void dfs(int x, int w, int f){  int c = a[x]&w;bitset<35> b(c);an[b.count()]++;for (int i : e[x]) {if (i==f) continue; dfs(i,c,x); }
}
void slove() {int n, q;cin >> n >> q;for (int i = 1; i <= n; i++) cin >> a[i];for (int i = 1; i < n; i++) {int u, v;cin >> u >> v;e[u].push_back(v);e[v].push_back(u); }dfs(1,-1,0);while (q--) {int x;cin >> x;cout << an[x] << endl;}
}
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

这题的题目描述真是一言难尽,连最基本的需求都表达不清楚;第一次用比赛的小喇叭,没想到出题人还真的回复了;后面补充过后明白题意后操作起来就很简单;


E-咕咕嘎嘎!!!(easy)

数据很小,而且输入的是一个数,一开始想的是dfs枚举,但估计会超时,于是就想动态规划或者数学推导;但是太菜了没推出来;

题解中给出的动态规划的写法,有点类似于暴力,三重循环,i表示枚举新选进去的数,j枚举选了这个数后所有gcd的情况,k表示已经选取的序列的长度有点类似01背包,看看是加上这一位更优还是不加更优,这题里面不牵扯物品的价值价值,所以直接加也可以;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=5e3+5;
const int M=1e9+7;
int dp[N][N];
void slove(){int n,m;cin>>n>>m;for(int i=2;i<=n;i++)dp[i][1]=1;for(int i=2;i<=n;i++){ // 枚举的是选进去的数for(int j=2;j<i;j++){ // 枚举的是前面状态的gcd是多少for(int k=m;k>=2;k--){ // 枚举级精选取得序列长度的长度,每一维都相当于是个01背包,所以倒序dp[__gcd(i,j)][k]=max(dp[__gcd(i,j)][k],dp[__gcd(i,j)][k]+dp[j][k-1]);dp[__gcd(i,j)][k]%=M;}}}int an=0;for(int i=2;i<=n;i++)an+=dp[i][m],an%=M;cout<<an;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

C-O神

这题感觉还是比较有意思的,不太好想,被题目中设立的情境给迷惑了;比赛的时候以为就是一个大模拟,但是赛后看题解才看出来居然是个背包dp;使用 dp[j]表示消耗 j能获得的最大收益。把最优的充值情况记录下来;然后看每一抽在最优的情况下需要花费多少的钱记录在新的数组b[]中;

但是这个样例看着太奇怪了,比赛的时候根本没看懂,正常的概率不应该是小数吗?后面发现是乘法逆元的缘故:

在普通数学中,概率 pq\frac pqqp是一个小于等于1的分数。但在模数下,分数pq\frac pqqp被表示为 p×q−1p\times q^{-1}p×q1 mod MMM,其中 q−1q^{-1}q1qqq 的模逆元。模逆元q−1q^{-1}q1通常是一个很大的数(因为qqq是小整数时,q−1modMq^{-1}\mod Mq1modM接近MMM)。期望计算中,每一项b[i]×p×qkb[i]\times p\times q^kb[i]×p×qk在模数下会被放大p=pqp= \frac pqp=qp在模数下是p×q−1modMp\times q^-1\mod Mp×q1modM(大数)。qkq^kqk(1−p)kmodM(1-p)^k\mod M(1p)kmodM,但通常仍是大数。最终累加时,这些大数相乘再取模,结果可能仍然很大。进一步了解好像是大二会学到的离散;

对概率逆元操作完之后,把每一抽的期望[总概率(成功的概率×当前失败的概率)×代价]计算出来累加就好了!

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=2e3+3;
const int M=1e9+7;
int dp[N],b[N];
pii a[N];
int kksu(int a,int b){a%=M;int s=1;while(b){if(b&1){s*=a;s%=M;}a*=a;a%=M;b>>=1;}return s;
}
void slove(){int x,p,q,m;cin>>x>>p>>q>>m;int n;cin>>n;for(int i=1;i<=n;i++)cin>>a[i].fi>>a[i].se;for(int i=1;i<=n;i++)for(int j=N;j>=a[i].fi;j--)dp[j]=max(dp[j],dp[j-a[i].fi]+a[i].se);int t=0;for(int i=1;i<=m;i++){while(dp[t]<x*i)t++;b[i]=t;}int p1=p*kksu(q,M-2)%M;int q1=((1-p1)%M+M)%M;t=1;int an=0;for(int i=1;i<m;i++){an+=b[i]*p1%M*t%M;an%=M;t*=q1;t%=M;}an+=t*b[m];cout<<an%M;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

但是代码里面的细节超级多,超级容易写错,因为逆元的缘故,所以基本上计算的地方都要一直去取模;


B-异或期望的秘密

这题没看懂的原因和上一题一样,不明白样例里面为什么突然变得那么大,还是对乘法逆元的理解不够深刻!

看懂后其实不难理解;

再具体的看这题之前先看下面这两题;


小蓝的二进制询问

D-小蓝的二进制询问_河南萌新联赛2024第(一)场:河南农业大学

不难发现,这道题也是这个人出的

题目要求计算区间 [l, r]内所有整数的二进制表示中1的个数之和的问题。核心策略是利用按位计算的方法高效地计算从0到任意整数n(包括n)的二进制1的个数之和,然后通过前缀和思想计算区间和。

整体的思想就是类似于前缀和的思想对于查询 [l, r],区间和等于 f(r) - f(l-1),即:
ans=(∑i=0rpopcount(i))−(∑i=0l−1popcount(i))\text{ans} = (\sum_{i=0}^{r} \text{popcount}(i)) - (\sum_{i=0}^{l-1} \text{popcount}(i))ans=(i=0rpopcount(i))(i=0l1popcount(i))

f(n)计算从0到n(包括0和n)的所有整数的二进制表示中1的个数之和。

循环条件:变量 t从2开始,每次左移一位(即t * =2),直到t>=n * 2。

周期贡献:对于每个位位置 (相当于以 t为周期),完整周期的个数是n/t。每个周期中该位为1的个数是 t/2。因此,完整周期贡献为:(n/t)*(t/2)。

以i=2,第二位为例

十进制数  二进制   第2位
0        000      0  ← 周期开始
1        001      0
2        010      0
3        011      0 
4        100      1
5        101      1
6        110      1
7        111      1
8       1000      0   ← 下一个周期开始
...

周期长度:T = 2i+1 = 8
每个周期内:前2i个数第2位为0,后4个数为1

所以得到每一位的周期就是T = 2i+1,周期内1的个数就是2i

剩余部分贡献:在一个不完整的周期中,如果余数n%t大于 t/2,则剩余部分中该位为1的个数是 n%t- t/2。

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
const int M=998244353;
int f(int n){if(n==0) return 0;int s=0,t=2;n++;while(t<n*2){s+=n/t*(t/2);if(n%t>t/2) s+=n%t-t/2;s%=M;t<<=1;}return s%M;
}
void slove(){int l,r;cin>>l>>r;cout<<(f(r)-f(l-1)+M)%M<<endl; 
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;cin>>_;while(_--)slove();return 0;
}

累加器

F-累加器_河南萌新联赛2024第(三)场:河南大学

这题要让我们看从x到x+y这期间,对应二进制的数的每一位;可以延续上一题的思路;我们看看从0到x+y中变了多少次再减去从0到x变了多少次,就是我们的结果;

这道题我们只用找到他变化的周期就行,不要再具体看有几个1了,所以更简单点;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
const int M=998244353;
int f(int x){int s=0;int t=1;while(t<=x){s+=x/t;t<<=1;}return s;
}
void slove(){int x,y;cin>>x>>y;cout<<f(x+y)-f(x)<<endl;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;cin>>_;while(_--)slove();return 0;
}

异或期望的秘密

现在我们再回来看这道题;这题看着带这个异或的字样,其实和异或关系并不大;异或时相同的位会被赋予0,所以我们去找不同的位的个数,再去比上区间长,就是我们所求的期望;

为了方便统计,我们就一位一位的看,看看这个区间的数的这一位上有几个1几个0,再看参与运算的k这一位是1还是0,找到不一样的那个数的个数,累加的分子里面;最后计算出的结果是个分数,题目要求输出逆元的形式;和上面的推导是一样的;这里每次f函数判断一位就行,遍历每一位的for在主函数中;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
const int M=1e9+7;
int kksu(int a,int b){int s=1;while(b){if(b&1){s*=a;s%=M;}a*=a;a%=M;b>>=1;}return s;
}
int f(int n,int i){if(n==0) return 0;int s=0,t=1<<(i+1);n++;s+=n/t*(t/2);if(n%t>t/2) s+=n%t-t/2;s%=M;return s%M;
}
void slove(){int l,r,k;cin>>l>>r>>k;int fz=0,fm=r-l+1;for(int i=0;i<=30,(1ll<<i)<=max(k,r);i++){int v=f(r,i)-f(l-1,i);if(k>>i&1) v=fm-v;fz+=v;fz%=M;}int an=fz*kksu(fm,M-2)%M;cout<<an<<endl;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;cin>>_;while(_--)slove();return 0;
}

A-约数个数和

朴实无华的题目描述,朴实无华的模板;一开赛就看到好多人都过了,题目还这么简短;那大概率就是模板了;一开始是毫无头绪的,因数的分解一直不太会(后来才想明白为什么是除),比赛的时候列了超级多的例子,惊奇的发现我们要求的答案就是∑i=1nni\sum_{i=1}^{n}\frac{n}{i}i=1nin,当时可激动了于是就暴力提交;但是数据很大超时了;于是就把每一项都列了出来,想到了之前看到过的整除分块,但是之前没具体学,好在还记得当时的思路自己推了推;

看似很简单,但是当时写完d后就一直在推推推…;好在最后还是对了;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
void slove(){int n;cin>>n;int an=0;for(int l=1;l<=n;l++){int r=n/(n/l);an+=(n/l)*(r-l+1);l=r;}cout<<an;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

整除分块

什么是整除分块?

整除分块 (又称数论分块) 是一种用于高效计算涉及整除运算的和式的技巧,常见于数论和算法问题中。其核心思想是将求和式中相同的整除结果进行“分块”,合并计算,从而将时间复杂度从O(n)O(n)O(n)优化至O(n)O(\sqrt{n})O(n)

给定一个正整数nnn ,如何快速计算以下和式?

S=∑k=1n⌊nk⌋S=\sum_{k=1}^n\lfloor\frac{n}{k}\rfloor S=k=1nkn

我们以16为例,现将结果列出来

i1234567891011121314151616i201065432222111111\begin{array}{|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|c|} \hline i & 1 & 2 & 3 & 4 & 5 & 6 & 7 & 8 & 9 & 10 & 11 & 12 & 13 & 14 & 15 & 16 \\ \hline \frac{16}{i} & 20 & 10 & 6 & 5 & 4 & 3 & 2 & 2 & 2 & 2 & 1 & 1 & 1 & 1 & 1 & 1 \\ \hline \end{array}ii1612021036455463728292102111121131141151161

观察整除结果:对于 k∈[1,n]k\in[1,n]k[1,n],⌊nk⌋的取值最多只有2n\left\lfloor\frac nk\right\rfloor\text{的取值最多只有}{2\sqrt{n}}kn的取值最多只有2n种(因为 k≤n时有n种取值,k>n时⌊nk⌋≤n)k\leq\sqrt{n}\text{时有}{\sqrt{n}}\text{种取值,}{k>\sqrt{n}}\text{时}{\left\lfloor\frac nk\right\rfloor\leq\sqrt{n}})kn时有n种取值,k>nknn)

合并相同值:找到所有满足⌊nk⌋=q的连续区间 [l,r]\left\lfloor\frac nk\right\rfloor=q\text{的连续区间 }{[l,r]}kn=q的连续区间 [l,r],计算该区间的贡献为 q⋅(r−l+1)q\cdot(r-l+1)q(rl+1)

确定右端点:对于当前分块的左端点l,其右端点为r=∣nq∣l\text{,其右端点为}r=\left|\frac nq\right|l,其右端点为r=qn,其中q=⌊nl⌋q=\left\lfloor\frac nl\right\rfloorq=ln

看不懂?没关系来数形结合,画出16i\frac{16}{i}i16的函数图像,就是我们熟悉的反比例函数的图像;因为计算机的除法会向下取整,所以我们画出向下取整的函数,不难发现也是一段一段的!
与我们发现的规律一致;
函数图像

所以整除分块就是找到这些连续的块的左右端点,这样求和的时候就不用每个数都遍历一遍了;

我们可以这么理解,因为是向下取整,所以顺序遍历新得到的结果的数可能是除不尽的(可能会有余数),那我们就反除这个结果,得到恰好能除完的位置,那个位置就是我们的右端点;再往后就是新的结果了;

分块过程还是以16为例

分块区间 [l, r]q = ⌊16l⌋\left\lfloor\frac{16}{l}\right\rfloor l16右端点 r=⌊16q⌋r = \left\lfloor\frac{16}{q}\right\rfloor r=q16贡献 q⋅(r−l+1)q \cdot (r - l + 1) q(rl+1)
[1, 1]16116 × 1 = 16
[2, 2]828 × 1 = 8
[3, 3]535 × 1 = 5
[4, 4]444 × 1 = 4
[5, 5]353 × 1 = 3
[6, 8]28(因为 ⌊162⌋=8\left\lfloor\frac{16}{2}\right\rfloor = 8 216=82 × 3 = 6
[9, 16]1161 × 8 = 8

所以就能得到模板

for(int l=1;l<=n;l++){int r=n/(n/l);an+=(n/l)*(r-l+1);l=r;
}

同类练习

K-取模_2022年中国高校计算机大赛-团队程序设计天梯赛(GPLT)上海理工大学校内选拔赛

也是一道数学题;题目要求∑i=1n(n%i)\sum_{i=1}^{n}\left(n\%i\right)i=1n(n%i)的结果;

我们可以得出n%i=n−⌊ni⌋⋅in\%i=n-\lfloor\frac{n}{i}\rfloor\cdot in%i=nini

我们的问题就可以转行为∑i=1n(n%i)=∑i=1n(n−⌊ni⌋⋅i)\sum_{i=1}^{n}(n\%i)=\sum_{i=1}^{n}(n-\lfloor\frac{n}{i}\rfloor\cdot i)i=1n(n%i)=i=1n(nini)也就是=n2−∑i=1n(⌊ni⌋⋅i)=n^{2}-\sum_{i=1}^{n}(\lfloor\frac{n}{i}\rfloor\cdot i)=n2i=1n(⌊ini)

只看右边进而得到∑x=lrx⋅⌊ni⌋\sum_{x=l}^{r}x\cdot\lfloor\frac{n}{i}\rfloorx=lrxin所以我们要求的就是整除分块后每一块的区间和(用等差数列求和)×这一块的值;最后累加起来用n2减;就可以了;

#include<bits/stdc++.h>
using namespace std;
#define int long long
#define endl '\n'
#define pii pair<int,int>
#define fi first
#define se second
const int inf=0x3f3f3f3f;
const int N=1e5+5;
const int M=998244353;
void slove(){int n;cin>>n;__int128 s=0;for(__int128 l=1,r=1;l<=n;l++){int k=n/l;r=n/k;s+=((l+r)*(r-l+1)/2*k+M)%M;l=r;}int an=((__int128)n*(__int128)n-s)%M;cout<<an;
} 
signed main(){ios::sync_with_stdio(0);cin.tie(0);cout.tie(0);int _=1;//cin>>_;while(_--)slove();return 0;
}

L 题也是数论的题,推出结论就能快速解决,可结论成了大问题

H 看题解说是个数位dp,好像也能做?

G 题树形dp,比赛的时候看出来了,和之前写的没有上司的舞会比较像,但是写的时候有bug一直改不对;


这次补题的收获还是很大的,也学到了很多新的东西;


http://www.dtcms.com/a/296859.html

相关文章:

  • 使用Docker+Nginx部署电商平台项目(服务端+管理端+商城)
  • 基于STM32智能鱼缸监控投喂系统
  • Kubernetes 集群架构和Pod创建流程
  • 优选算法:移动零
  • 激光雷达的单播和广播模式介绍
  • 2025年海外短剧独立站开发:H5+PC端双平台技术实践与增长策略
  • 处理HTTP请求体:精通`@RequestBody`、`@RequestHeader`与`@CookieValue`
  • 计算机视觉技术剖析:轮廓检测、模板匹配及特征点匹配
  • SpringBoot框架简介
  • Windows本地部署DeepSeek
  • git更新内核补丁完整指南
  • 【C++】使用中值滤波算法过滤数据样本中的尖刺噪声
  • Java 并发容器:ConcurrentHashMap 笔记(JDK 1.8)
  • 01_FOC学习之先让电机转动起来
  • 双紫擒龙紫紫红黄安装使用攻略,2025通达信指标源码,擒龙追踪源码公式学习
  • 爬虫基础概念
  • 浏览器访问[http://www.taobao.com](http://www.taobao.com/),经历了怎样的过程。
  • DNS域名解析过程
  • 通达OA二次开发
  • Impact rating 影响等级定义(学习笔记)
  • YOLOv8 剪枝模型加载踩坑记:解决 YAML 覆盖剪枝结构的问题
  • 【JAVA】使用vosk实现windows实时语音转文字,解放双手
  • vs2019 创建MFC ActiveX的详细步骤
  • JS事件基础
  • ESP-NOW无线通信协议:物联网设备间的高效对话方式
  • 前端基础知识Vue系列 - 24(axios的原理)
  • Linux(centos7)安装 docker + ollama+ deepseek-r1:7b + Open WebUI(内含一键安装脚本)
  • Windows下使用UIAutomation技术遍历桌面窗口和指定窗口内容的AutomationWalker.exe的C#源代码
  • QT元对象系统-(1)静态属性和动态属性
  • Jenkins配置与应用指南