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

河南萌新联赛2025第(二)场:河南农业大学(整除分块,二进制,树的搜索)

文章目录

    • @[toc]
  • A、约数个数和(整除分块)
    • 思路
    • 代码
  • 扩展:取模(整除分块)
    • 思路
    • 代码
  • B、异或期望的秘密
    • 二进制位的周期性规律
    • 核心思路
    • 代码详细解释
      • 1. 快速幂函数 qpow
      • 2. 统计函数 count
      • 3. 主逻辑 solve
    • 完整代码
  • D、开罗尔网络的备用连接方案
    • 思路
      • 建树
      • 搜索
  • 扩展:插排串联(树的搜索)
    • 题目大意
    • 思路
    • 代码
  • I、猜数游戏
    • 代码
  • K、打瓦
    • 代码
  • M、米娅逃离断头台
    • 思路
    • 代码
  • 小结

When the sharpest words wanna cut me down
I’m gonna send a flood, gonna drown them out
I am brave, I am bruised
I am who I’m meant to be, this is me

​ ——《This Is Me》(the theme song of 《The Greatest Showman》)

这次比赛签到题没写出来,整的我都不敢往后开了。感觉是不是最近写题wa的太少了,碰到wa的题都不敢花时间改。还是上次的感受,题目的质量依旧非常的高,有几道模版题在之前都能看到他们的影子。上次我就花了一整天时间补题,但是感觉效率稍微有点低,真正汲取到的东西还不够,从这次开始要放更多重心在补题上。因为补的题都是可以够得上的,而且是真实会考而且已经考出来的东西,趁着比赛认真学习一下应该记忆会比较清晰。


A、约数个数和(整除分块)

来源:A-约数个数和_河南萌新联赛2025第(二)场:河南农业大学

思路

这个题其实还是非常简单的,主要分为两部分:第一部分:我们正常来想可能是分解为1:1;2:1,2;3:1,3····· 这样来思考。但是这样的复杂度就是n²。但是可以发现的是有很多因数都是重复的。如果我们按照因数来计算的话也许就会简化的多了。思考一下,1n的因数也可能是1n。举个例子,因数有2的数(也就是2的倍数)是每两个出现一次,而我们的左边界又是1,可以大胆的计算出因数2的个数就是n/2因数有3的数(也就是3的倍数)是每三个出现一次。那么我们就可以大胆进行推测,在整个求和序列中因数x的出现次数就是 n/x 次。到了这里这一题就完成了一半了。第二部分:通过第一部分的归纳,我们可以简单将程序归纳为:
∑i=1n[ni]\sum_{i=1}^{n}\left [\frac{n}{i} \right] i=1n[in]
但是我们的n最大是2^31,大概就是2e9的范围,直接相加肯定是会超时的,此时就说到了我们的整除分块了。例如此时n=20:

i1234567891011121314151617181920
n/i2010654322221111111111

我们会发现我们要相加的这些部分其实有许多是重复的,由于乘法向下取整的原因,只要不满足上一个边界那么就会掉到下一个数。如果我们直接求解出这一段数的左右边界的话就不用一个个相加了。从2-1的来枚举吧,根据数学知识就可以看出,当 i 被 n刚好整除的时候它对应的 i 就是这段数的右边界n/(n/i),如果再小就会向下取整了。左边界虽然直接不好求,但是这么来想:上一个区间右边界的下一个不就是此区间的左边界嘛。可以找出区间的长度那么每个不同的 n/i 就只用遍历一次就可以了。这样跳着求的话操作的次数就会极大的减少了。

其实这个也不算什么算法,只要对除法稍微有点了解就能推出来。我当时脑子非常的混乱,再加上签到题一直没过,写后边的题一直在想。这个题第一部分很快就想到了,后边一直把这一步和第一部分混在一起,整的非常复杂。如果状态好点的话我相信肯定可以把这个推出来的!

代码

void solve()
{cin >> n;for(int i=1; i<=n; i++){r=n/(n/i);//右边界就是刚刚好不会向下取整的那个点ans+=(n/i)*(r-l);//本区间的值✖️本区间的区间大小求本区间的总和l=r;//本次的右区间确定下一次的左区间i=l;//更新i值跳到下一个区间}cout << ans;
}

扩展:取模(整除分块)

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

思路

需要先推导一下公式:
∑i=1n(n%i)=∑i=1n(n−⌊ni⌋×i)=n2−∑i=1n⌊ni⌋×i\sum_{i=1}^{n}(n \% i)=\sum_{i=1}^{n}\left(n-\left\lfloor\frac{n}{i}\right\rfloor \times i\right)=n^{2}-\sum_{i=1}^{n}\left\lfloor\frac{n}{i}\right\rfloor \times i i=1n(n%i)=i=1n(nin×i)=n2i=1nin×i

i1234567891011121314151617181920
[n/i]*i2020182020181416182011121314151617181920

有没有发现什么规律?其实并没有规律😄最主要的还是处理[n/i],至于乘i可以求得此区间大小后用等差数列直接处理

需要注意的是:n*n会超long long,需要用_ _int128进行存储,中间还要格外注意别爆范围了

代码

void solve ()
{int l=0,r=0;cin >> n;m=n;__int128 sum=(__int128) m*m; // 强制转换为__int128 避免 n 较大时乘积溢出for (int l=1; l<=n; l++){r=n/(n/l);sum-=((__int128)(l+r)*(r-l+1)/2)*(n /l);l=r;}int ans=(sum % mod + mod) % mod;//别减成负数了cout << ans;
}

B、异或期望的秘密

来源:B-异或期望的秘密_河南萌新联赛2025第(二)场:河南农业大学

本题是一个有关二进制规律的题,也是比较模板,本题就来介绍一下。

可以结合本篇博客:关于二进制的规律-CSDN博客

二进制位的周期性规律

二进制位的变化具有周期性。以第 k 位为例:

  • 周期长度为 2^(k+1)
  • 在每个周期内,前 2^k 个数的第 k 位为 0,后 2^k 个数的第 k 位为 1

例如:

  • k=0(最低位)时,周期长度为 2^1=2,序列为 0,1,0,1,0,1,...
  • k=1(次低位)时,周期长度为 2^2=4,序列为 0,0,1,1,0,0,1,1,...
  • k=2(第三位)时,周期长度为 2^3=8,序列为 0,0,0,0,1,1,1,1,...

核心思路

  1. 数学期望分解: 二进制中每一位的贡献是独立的。对于第 k 位,其对总期望的贡献为 该位为 1 的概率。因此,总期望等于所有位的概率之和。
  2. 位运算规律x XOR y 的第 k 位为 1,当且仅当 x 的第 k 位与 y 的第 k 位不同。
    • y 的第 k 位为 0,则 x 的第 k 位需为 1;
    • y 的第 k 位为 1,则 x 的第 k 位需为 0。
  3. 快速统计区间内位为 1 的数目: 使用 count 函数高效计算区间 [0, x] 中第 k 位为 1 的数的个数,利用二进制位的周期性规律,避免遍历整个区间。

代码详细解释

1. 快速幂函数 qpow

int qpow(int a, int b)
{int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans;
}
  • 功能:计算 a^b % mod,用于求模逆元(根据费马小定理,a 在模 mod 下的逆元为 a^(mod-2))。
  • 原理:快速幂算法,时间复杂度 (O(log b))。

2. 统计函数 count

int count(int k, int x)//求0~x区间内第k位有多少1
{int t=1LL<<(k+1);//第k位周期int full=(x+1)/t;//有多少个整周期(总数除周期长度)int re=(x+1)-t*full;//还剩下几个return full*t/2+max(0LL, re-t/2);//整周期中1的个数+剩余一段1的个数//例如k=2:周期为0,0,0,0,1,1,1,1·····//周期为8,可以观察到一个周期内前一半为0,后一半为1根据此规律进行计算
}
  • 功能:计算区间 [0, x] 中第 k 位为 1 的数的个数。
  • 原理:
    1. 周期规律:第 k 位的变化周期为 2^(k+1),每个周期前半部分为 0,后半部分为 1。
    2. 完整周期贡献:每个完整周期包含 t/2 个 1。
    3. 剩余部分处理:若剩余长度超过周期的一半,则贡献 re - t/2 个 1。

3. 主逻辑 solve

void solve()
{cin >> l >> r >> y;ans=0;int len=r-l+1;int inv=qpow(len,mod-2);//因为要除以len,所以求一下它的逆元for(int k=0; k<=30; k++){int cnt1=count(k,r)-count(k,l-1);//(0~r)-(0~l-1)=(l~r)int cu=(y>>k)&1;//y的第k位是0 or 1?if(cu) cnt=len-cnt1;//为使异或值为1,需要反着来else cnt=cnt1;      //如果y当前位为0就统计1的个数,不然就统计0的个数ans=(ans+cnt*inv)%mod;//加上当前位概率}cout << ans << endl;
}
  • 输入处理:读取区间 [l, r] 和整数 y
  • 模逆元计算:由于期望计算需要除以区间长度 len,在模运算中转换为乘以 len 的逆元。
  • 逐位计算:
    • 统计区间内第 k 位为 1 的数目:通过 count 函数差分得到 [l, r] 中第 k 位为 1 的数的个数 cnt1
    • 判断异或条件:根据 y 的第 k 位的值,决定需要统计 x 的第 k 位为 0 还是 1 的数目。
    • 累加贡献:将当前位的概率贡献 cnt/len 转换为模运算形式 (cnt * inv) % mod

完整代码

int qpow(int a, int b)
{int ans=1;while(b){if(b&1)ans=ans*a%mod;a=a*a%mod;b>>=1;}return ans;
}
int count(int k, int x)//求0~x区间内第k位有多少1
{int t=1LL<<(k+1);//第k位周期int full=(x+1)/t;//有多少个整周期(总数除周期长度)int re=(x+1)-t*full;//还剩下几个return full*t/2+max(0LL, re-t/2);//整周期中1的个数+剩余一段1的个数//例如k=2:周期为0,0,0,0,1,1,1,1·····//周期为8,可以观察到一个周期内前一半为0,后一半为1根据此规律进行计算
}
void solve()
{cin >> l >> r >> y;ans=0;int len=r-l+1;int inv=qpow(len,mod-2);//因为要除以len,所以求一下它的逆元for(int k=0; k<=30; k++){int cnt1=count(k,r)-count(k,l-1);//(0~r)-(0~l-1)=(l~r)int cu=(y>>k)&1;//y的第k位是0 or 1?if(cu) cnt=len-cnt1;//为使异或值为1,需要反着来else cnt=cnt1;      //如果y当前位为0就统计1的个数,不然就统计0的个数ans=(ans+cnt*inv)%mod;//加上当前位概率}cout << ans << endl;
}

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

来源:D-开罗尔网络的备用连接方案_河南萌新联赛2025第(二)场:河南农业大学

本题是一道相对模板的树的遍历,原来题目意思非常难懂(其实就是有问题),后来改了题面我也没看。其实还是比较基础的,刚好趁此机会学习一下树。

思路

只要按照题目的意思将整棵树给遍历一下就行了,在遍历的过程中记录此时的权值。主要是分为

建树

由于题中说的是要建无向树,所以可以用vector类型的数组进行存储,将两个点都互相存一下。

for(int i=1; i<n; i++)
{cin >> u >> v;t[u].push_back(v);//将两个点都互相存一下t[v].push_back(u);
}

搜索

对自己可以到达的点直接进行暴搜,注意别让它回头,不然会出现死循环。至于为什么初始的&值要赋为-1,这就要牵扯到二进制的一些知识了。

在二进制中,负数的表示并非简单地在正数前加符号,而是通过补码(Two’s Complement) 来实现,这是计算机中最常用的方式(目的是统一加减法运算)。

正数的补码就是它自己;

负数的补码:

  • 先取对应正数的原码;
  • 对原码按位取反(0 变 1,1 变 0),得到反码;
  • 反码加 1,结果即为负数的补码。

对于7:

+70000 0111正数原码
-71111 1001① 取反 11111000 → ② 加 1 得 11111001

那么对于-1:1的原码是0000 0001,① 取反 1111 1110 → ② 加 1 得 11111111

这样一来-1的二进制就全是1了,而&是收集0的个数,-1&x=x。

dfs(1,-1,0);//(主函数里)
void dfs(int x, int y, int z)//当前节点,之前的&值,上一个节点
{int c=a[x]&y;bitset<32> b(c);mp[b.count()]++;for(auto i:t[x]){if(i==z) continue;//避免再回去形成死循环dfs(i,c,x);}
}

扩展:插排串联(树的搜索)

来源:辽宁CCPC——插排串联

题目大意

​ 大概的意思是说,有一个插座上连了一个插排,而插排之间又相互连接,最后连了用电器。给了每个插排最大的额定功率以及用电器的额定功率,让你判断是否可以通过一定的顺序交换使得整个东西可以正常运行。

思路

输入的时候可以直接把插排的额定功率都给存起来,然后通过搜索将所有插排的实际功率给存起来(插排的实际功率等于它所能到达的所有用电器的额定功率之和)。让他们分别进行排序,然后一一进行对照比较。如果有对照的双方实际功率大于额定功率的话那就一定不行,不然就可以。值得注意的是,整个系统所有的用电器额定功率之和不能超过2200W。

代码

// Problem: 插排串联
// Contest: Virtual Judge - Gym
// URL: https://vjudge.net/problem/Gym-105481C
// Memory Limit: 1024 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)
int a[N];//存各个东西的额定功率
vector<int> q[N];//建树
map<int, int> mp;//存插排
vector<int> aa;//存实际功率
vector<int> bb;//存额定功率
int n;//节点个数
int sum;//存总功率
int dfs(int x)
{if(q[x].size()==0)//此时是叶子节点{sum+=a[x];return a[x];}int qq=0;for(auto i:q[x])//将它所连的用电器的额定功率加起来就是它的实际功率qq+=dfs(i);if (x!=0) // 非根非叶子节点aa.push_back(qq);return qq;
}
void solve()
{cin >> n;for(int i=1; i<=n; i++){int u,v;cin >> u >> v;q[u].push_back(i);//建树mp[u]++;//将插排存起来a[i]=v;//存该节点的额定功率}for(auto i:mp){if(i.fi==0) continue;//不算根节点bb.push_back(a[i.fi]);}	dfs(0);//开始搜索找每个节点实际功率if(sum>2200){cout << "NO";return ;}sort(aa.begin(),aa.end());//排序后只要一一对应即可sort(bb.begin(),bb.end());for(int i=0; i<aa.size(); i++){if(aa[i]>bb[i])//实际功率不能大于额定功率{cout << "NO";return ;}}cout << "YES";
}

I、猜数游戏

这是一道签到题,能想到用二分,但是用几种形式都打了一遍交上去都不太对,后边就不想看了。没想到这么巧,感觉直接log有一点点猜的成分。

代码

void solve()
{cin >> n;int k=log2(n);if((int)pow(2,k)<n) cout << k+1;else cout << k;
}

K、打瓦

签到题,直接输出。

代码

void solve()
{cin >> s;cout << "gugugaga";
}

M、米娅逃离断头台

来源:M-米娅逃离断头台_河南萌新联赛2025第(二)场:河南农业大学

思路

这是一道简单的数学题,经过简单的公式推导就出来了,记得用double,保留两位小数。

设小圆半径为a,大圆半径为b。则可得推导式:
x=2b2−a2x=2\sqrt{b²-a²} x=2b2a2
可以推导出阴影面积的公式S:
S=π(b2−a2)2=π(x2)8S=\frac{\pi(b²-a²)}{2} =\frac{\pi(x²)}{8} S=2π(b2a2)=8π(x2)

代码

void solve()
{cin >> k;double dou=(k/2)*(k/2);double pai=dou*3.1415926535/2;cout << fixed << setprecision(2) << pai;
}

小结

花了好长时间来写这篇博客,可以说是知识最密集的一篇了。第一次学习之后记忆肯定非常的浅,后边还要来复习啊~

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

相关文章:

  • C++ explicit 上下文相关转换
  • 牛客多校04L :Ladder Challenge
  • 基于MASAC算法的建筑群需求响应系统设计与实现
  • 个人电脑 LLMOps 落地方案
  • pytest官方Tutorial所有示例详解(二)
  • 【AI】Java生态对接大语言模型:主流框架深度解析
  • FastAPI中间件
  • 如何在 conda 中删除环境
  • 常见半导体的介电常数
  • 告别下载中断:深入解析Tomcat JSP中的“远程主机强迫关闭连接”与“软件中止连接”
  • 理解传统部署下 Tomcat 核心组件与请求链路全流程
  • 详解力扣高频 SQL 50 题之584. 寻找用户推荐人【入门】
  • SpringBoot + Thymeleaf 实现模拟登录功能详解
  • SQL173 店铺901国庆期间的7日动销率和滞销率
  • 比例谐振控制器(PR控制器)在交流系统中的应用原理详细解析
  • Ubuntu安装jdk、上传jar包、运行java、配置域名、nginx接口映射、配置https域名
  • 一文读懂 HTTPS:证书体系与加密流程
  • HttpServletRequestWrapper存储Request
  • ORACLE DATABASE 23AI+Apex+ORDS -纯享版
  • 内网IM:BeeWorks私有化部署的安全通讯解决方案
  • 7.24路由协议总结
  • 使用Python采集招聘网站数据并智能分析求职信息
  • 11款Scrum看板软件评测:功能、价格、优缺点
  • 【News】同为科技亮相首届气象经济博览会
  • Ollama(5)服务接口压力测试
  • 【docker | 部署 】Jetson Orin与AMD平台容器化部署概述
  • 《 Spring Boot启动流程图解:自动配置的真相》
  • 美林数据用大模型重构电能质量评估,让隐蔽合规问题无所遁形
  • Lua(模块与包)
  • Lua循环流程控制