2025杭电多校第十场 Cut Check Bit、Multiple and Factor 个人题解
Multiple and Factor
根号分治 #数学
题目

思路
本题采用根号分治的思想,令\(B=\sqrt{ n }\),将下标分为\(1\leq i\leq B\)与\(B<i\leq n\)两类数进行维护
数组\(a[N]\)用于储存初始权值
操作一:令\(x\)的所有倍数位置\(+k\)
- 若\(x\leq B\):
- 创建数组\(add[B]\),作为小数倍数和的加法懒标记
- 直接 \(add[x]+=k\)即可
- 时间复杂度\(o(1)\)
- 若\(x>B\):
- 可知此时的\(x\)的倍数个数不会超过\(B\)个,因此可以暴力解决
- 枚举\(x\)的所有倍数\(tx\),\(a[tx]+=k\)
- 时间复杂度\(o(B)\)
操作二:令\(x\)的所有因数位置\(+k\)
- 直接枚举\(x\)的所有因数\(d\)
- \(a[d]+=k\ [\ d\ |\ x\ ]\)
- 时间复杂度\(o(\sqrt{ n })\)
操作三:查询\(x\)的所有倍数位置的权值和
- 若\(x>B\):
- 先暴力枚举\(x\)的所有倍数\(tx\),\(ans+=a[tx]\)
- 接下来需要处理\(1\leq i\leq B\)部分的加法懒标记:
- 设集合\(S_{i}=\{ j\ |\ j=t\times i\ ,\ j\leq n\ ,\ t\geq 1 \}\),表示所有小于\(n\)的\(i\)的倍数集合
- 可知\(S_{i}\cap S_{x}=S_{lcm(x,i)}\)
- \(add[i]\)对答案的贡献即\(add[i]\times S_{lcm(x,i)}.size\)
- 而\(S_{j}.size=\left\lfloor \frac{n}{j} \right\rfloor\)
- 因此\(ans+=add[i]\times \left\lfloor \frac{n}{lcm(x,i)} \right\rfloor\)
- 时间复杂度\(o(B+B\log n)\),\(\log n\)来自\(lcm(x,i)\)
- 若\(x\leq B\):
- 这些数不好用懒标记维护,所以直接每次修改都暴力维护
- 创建数组\(addmul[B]\),用于直接储存操作三小数询问的答案
- 进行操作一时:(令\(x\)的所有倍数位置\(+k\))
- 考虑直接更新\(1\leq i\leq B\)的\(addmul[i]\)
- 同样考虑\(S_{i}\)与\(S_{x}\)两个集合,可得\(addmul[i]+=k\times\left\lfloor \frac{n}{lcm(x,i)} \right\rfloor\)
- 时间复杂度\(o(B\log n)\)
- 进行操作二时:(令\(x\)的所有因数位置\(+k\))
- 考虑直接更新\(1\leq i\leq B\)的\(addmul[i]\)
- 首先\(i\)必须是\(x\)的因数才会被更新到,即\(i\ |\ x\)
- 设\(t\times i=x\),若\(d\ |\ t\),则\((d\times i)\ |\ x\),因此\(d\)的数量即为又是\(i\)的倍数又是\(x\)的因数的个数
- 创建数组\(d[B]\),\(d[i]\)表示数字\(i\)的因数个数,求\(d\ |\ t\)的个数即\(d[t]=d\left[ \frac{x}{i} \right]\)
- 因此\(addmul[i]+=k\times d\left[ \frac{x}{i} \right]\)
- 时间复杂度\(o(B)\)
操作四:查询\(x\)的所有因数位置的权值和
- 先暴力枚举\(x\)的所有因数\(d\),\(ans+=a[d]\)
- 接下来处理小数部分的加法懒标记:
- 对于\(1\leq i\leq B\)且\(i\ |\ x\),同样也是考虑“又是\(i\)的倍数又是\(x\)的因数的个数”
- 因此\(ans+=d\left[ \frac{x}{i} \right]\times add[i]\)
- 时间复杂度\(o(\sqrt{ n }+B)\)
对于数组\(d[B]\),可以通过类似欧拉筛的方法预处理:
- 枚举\(1\leq i\leq n\),对于每个\(i\)再枚举\(k\times i\leq n\),\(d[k\times i]++\)
- 对于每个确定的\(i\),将会枚举\(\left\lfloor \frac{n}{i} \right\rfloor\)个\(k\),因此总枚举次数为\(\sum_{i=1}^n\left\lfloor \frac{n}{i} \right\rfloor\)
- 复杂度为\(o\left( \sum_{i=1}^n\left\lfloor \frac{n}{i} \right\rfloor \right)=o\left( \sum_{i=1}^n \frac{n}{i} \right)=o\left( n·\sum_{i=1}^n \frac{1}{i} \right)\),由\(\sum_{n=1}^x \frac{1}{n}\leq 1+\ln x\)可知,复杂度为\(o(n\log n)\)(文末证明)
至此,由于\(B=\sqrt{ n }\),总复杂度来到\(o(n\log n+m·\sqrt{ n }·\log n)\),但由于\(\sqrt{ n }·\log n\)的原因,会\(TLE\)
因此考虑预处理\(gcd(a,b)\)从而将\(\log n\)消去:
- 因为调用时\(lcm(x,i)\)中\(1\leq i\leq B\),而对于\(x>B\),辗转相除一次之后也将变为\(x\leq B\) ,因此仅预处理所有\(1\leq a,b\leq B\)的\(gcd(a,b)\)
- 枚举所有\(1\leq i<j\leq B\),复杂度为\(o(B^2)=o(n)\)
预处理后复杂度为\(n\log n+m·\sqrt{ n }\) 可以通过!
代码实现
#include<iostream>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
#include<set>
#include<stack>
#include<unordered_map>
using namespace std;
using ll = long long;
#define int ll
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
const int N = 5e5 + 5, M = 710;
int n, m, a[N], gcd[M][M], B, add[M], d[N], ansmul[M];
constexpr void init() {
rep(i, 1, B) {
rep(j, i + 1, B) {
gcd[i][j] = __gcd(i, j);
}
}
rep(i, 1, n) {
for (int j = i;j <= n;j += i)d[j]++;
}
}
constexpr int g(int x, int y) {
if (x == y)return x;
if (x > y)swap(x, y);
if (x <= M - 1 && y <= M - 1 && gcd[x][y])return gcd[x][y];
return __gcd(x, y);
}
constexpr int lcm(int x, int y) {
return x * y / g(x, y);
}
void eachT() {
cin >> n >> m;
B = sqrt(n);
init();
rep(i, 1, n) cin >> a[i];
rep(i, 1, B) {
for (int j = i;j <= n;j += i)ansmul[i] += a[j];
}
while (m--) {
int op;cin >> op;
if (op == 1) {
int x, k;cin >> x >> k;
if (x <= B)add[x] += k;
else {
for (int i = x;i <= n;i += x) a[i] += k;
}
rep(i, 1, B)ansmul[i] += n / lcm(x, i) * k;
} else if (op == 2) {
int x, k;cin >> x >> k;
for (int i = 1;i * i <= x;i++) {
if (x % i != 0)continue;
a[i] += k;
if (i * i != x)a[x / i] += k;
}
rep(i, 1, B)if (x % i == 0)ansmul[i] += d[x / i] * k;
} else if (op == 3) {
int x;cin >> x;
int ans = 0;
if (x <= B) {
ans = ansmul[x];
} else {
for (int i = 1;i * x <= n;i++)ans += a[i * x];
rep(i, 1, B)ans += n / lcm(x, i) * add[i];
}
cout << ans << '\n';
} else {
int x;cin >> x;
int ans = 0;
for (int i = 1;i * i <= x;i++) {
if (x % i != 0)continue;
ans += a[i];
if (i * i != x)ans += a[x / i];
}
rep(i, 1, B) {
if (x % i != 0)continue;
ans += add[i] * d[x / i];
}
cout << ans << '\n';
}
}
}
signed main() {
// cout << sqrt(5e5) << '\n';
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll t = 1;
//cin >> t;
while (t--) {
eachT();
}
}
关于调和级数复杂度的证明
\]
对于这个级数,我们可以通过拉格朗日中值定理来证明其上界为\(o(\log x)\)级别:
&设b<a,由拉格朗日中值定理:\\ \\
&\exists \ \delta\in[b,a]\ 使得 \frac{f(a)-f(b)}{a-b}=f'(\delta)\\ \\
&令 f(x)=\ln x,则f'(x)=\frac{1}{x}\\ \\
&b\leq \delta\leq a,则 \frac{1}{\delta} \geq \frac{1}{a}\\ \\
&令a=n,b=n-1,则: \\ \\
&\ln \frac{n}{n-1}= \frac{\ln n-\ln (n-1)}{n-(n-1)}=\frac{1}{\delta}\geq \frac{1}{n}\\ \\
&\sum_{n=2}^x\ln \frac{n}{n-1}\geq \sum_{n=2}^x \frac{1}{n}\\ \\
&而\sum_{n=2}^ x \ln \frac{n}{n-1}=\ln \left( \prod_{n=2}^x \frac{n}{n-1} \right)=\ln x\\ \\
&\therefore \sum_{n=1}^x \frac{1}{n} =1+\sum_{n=2}^x \frac{1}{n}\leq 1+\ln x
\end{align}
\]
Cut Check Bit
位运算 #数学 #dp #数位dp
题目

思路
不是很懂为什么官方题解这么简短,莫非出题人是CCB领域大神?!
关于\(2^k\)进制数的特点:
这样的进制数实际上都可以在化为二进制后直接拼接在一起!
比如\(32=2^{5}\)进制的数\(x_{3}x_{2}x_{1}x_{0}(32)=x_{3}\times 32^{3}+x_{2}\times 32^{2}+x_{1}\times 32+x_{0}(10)\)
而一个数乘以\(2^{k}\)就相当于其二进制表示左移\(k\)位,因此直接将\(x_{3}x_{2}x_{1}x_{0}\)所对应的二进制数由高位到低位直接拼接起来即可得到其二进制表示!
题干需要将各个段中的数字相与后再相或,并且还需要小于\(x\),那么相或后的结果\(t\)就需要满足\(t\ |\ x=x\),即\(x\)为0的位置上\(t\)必须为0,\(x\)为1的位置上\(t\)没有限制
由于相与的性质,对于一个确定的分段,只要有一个数字在\(pos\)位上是0,那么相与出来的数在\(pos\)位上必然也是0,这就可以作为我们调控\(t\leq x\)的限制
若\(t\leq x\),则在\(x\)为0的位置上,\(t\)必须也为0;在\(x\)为1的位置上,\(t\)可以为0也可以为1
一个数位dp的技巧:
\(t\leq x\)等价于\(t<x+1\)
为什么要这样做?
- 在\(t\leq x\)的限制下,除了\(t<x\)时可以自由选择,需要额外考虑\(t=x\)时的“紧贴上界”情况
- 而在\(t<x+1\)的限制下,仅需要保证\(t\)严格小于\(x+1\)的情况,不存在“紧贴上界”一说
- 在\(t<x+1\)的限制下,更新时可以一直限制\(t\)当前位为0(后文解释)
创建四个数组用于数位dp:
- \(1\leq i\leq n\)
- \(pre[i]\)表示在处理第\(pos\)位时,\(1\sim i-1\)中距离\(i\)最近的\(0\)的位置,即\((a[\ pre[i]\ ]\gg pos)\&1=0\);换一种说法,\(pre[i]\)表示仅考虑第\(pos\)位的情况下,以\(i\)位置作为一个段的结尾,这个段的最优左端点在\(pre[i]\)处
- \(l[i]\)表示考虑\(1\sim pos\)位上的限制,处理第\(pos\)位时,以\(i\)位置作为一个段的结尾,这个段的最优左端点在\(r[i]\)处
- \(dp[i]\)表示考虑\(1\sim pos\)位上的限制,处理第位时,\(1\sim i\)的最大分段数
- \(max[i]\)表示在处理第\(pos\)位时,\(1\sim i\)中的最大\(dp\)值
接下来需要理解一些过程:
- 设\(xbit\)为\(x+1\)在第\(pos\)位上的数字,\(tbit\)为段与段的\(OR\)和:\(t\) 在第\(pos\)位上的数字
- 不管\(xbit\)是什么,都先更新\(pre\)数组
- 若\(xbit=0\),则必须有\(tbit=0\),此时用\(pre\)数组更新\(l\)数组的限制,但不更新\(dp\)数组
- 若\(xbit=1\),则必须有\(tbit=0\)(看似与\(t=0 /1\)矛盾),此时\(dp\)数组由\(pre,l\)两个数组共同限制来更新,同时更新全局答案\(ans\),但\(l\)数组不更新
举一个例子来辅助理解:
以下数字均采用二进制,左端为高位右端为低位
- 设\(x+1=10010\)
- 由于高位对数字大小的影响更大,所以从高位向低位遍历
- \(pos=5\)时,\(xbit=1\):
- 理论上\(tbit=0 /1\),但是由于比较的是\(x+1\),\(t\)需要严格小于\(x+1\),所以\(t=0\),否则\(t=x=1\)相等了
- 通过限制\(tbit=0\),可以更新\(dp\)数组得到一次答案
- \(pos=4、3\)时,\(xbit=0\):
- 必须有\(tbit=0\)
- 通过\(pre\)来更新\(l\)数组的限制
- 此时不能更新\(dp\),因为\(x\)与\(t\)在这一位的值都是0,那么便有相等的风险!以\(pos=3\)为例,\(x:100\ ,\ t_{1}:100\ ,t_{2}:000\),此时更新\(dp\)将把\(t_{1}\)的答案算进去,因此错误
- \(pos=2\)时,\(xbit=1\):
- 若\(tbit=1\),则会出现\(x:1001\ ,\ t_{1}:1000,t_{2}:1001,t_{3}:0001,t_{4}:0000\)四种情况,其中\(t_{2}=x\)非法,再一次验证\(tbit=0\)的正确性
- 但若限制了\(tbit=0\),\(t_{3}:0001\)的情况不会漏掉吗?这也是合法的情况呀?
- 不会漏掉,因为本次不更新\(l\)数组,\(tbit=0\)的限制不会影响到后续的\(pos\)
- 正如\(pos=5\)时限制了\(tbit=0\),但是没有更新进\(l\)数组里,所以现在\(pos=2\)时,\(t\)中\(pos=5\)的位置就是\(0 /1\)
伪代码:
从高位向低位遍历\(pos\):
为了方便,将\(a_{i}\)在\(pos\)位上的值统称为\(i\)的值
- 预处理\(pre[i]\):
- 遍历\(1\leq i\leq n\)
- 若\(i\)的值为0,那么为了使得分段数尽可能多,则段长要尽可能小,因此最优左端点要尽可能靠近右端点,等于右端点是最优的,\(pre[i]=i\)
- 若\(i\)的值为1,那么只能从\(pre[i-1]\)转移过来,\(pre[i]=pre[i-1]\)
- 若\(xbit=0\):
- 遍历\(1\leq i\leq n\)
- 用\(l\)数组加入\(pre\)的限制:\(l[i]=min\{ l[i],pre[i] \}\)
- 若\(xbit=1\):
- 遍历\(1\leq i\leq n\)
- \(cur=min\{ l[i],pre[i] \}\),代表最优左端点位置
- 若\(cur=0\),说明在当前限制条件下无法分段,\(dp[i]=-1\)
- 否则\(dp\)由\(cur-1\)的状态转移而来:\(dp[i]=dp[\ max[cur-1]\ ]+1\)
- \(max[i]=max\{ max[i-1],dp[i] \}\)
- \(ans=max\{ ans,dp[n] \}\)
最后还需要特判\(x\)二进制为全1的情况,因为此时\(x+1\)只有最高位为1,其他都为0,而这个最高位有可能超出\(m\)的限制导致\(dp\)过程认为\(x+1=00\dots0 0\)出错
因此在\(x\)为全1的时候直接输出\(n\)即可~
代码实现
#include<iostream>
#include<vector>
#include<cmath>
#include<queue>
#include<algorithm>
#include<set>
#include<stack>
#include<unordered_map>
using namespace std;
using ll = long long;
// #define int ll
#define rep(i, a, b) for(int i = (a); i <= (b); i ++)
#define per(i, a, b) for(int i = (a); i >= (b); i --)
#define see(stl) for(auto&ele:stl)cout<<ele<<" "; cout<<'\n';
int n, m;
const int inf = 2e14;
bool readx(vector<int>& x) {
string s;cin >> s;
int cnt = 0;
rep(j, 1, m) {
char c = s[m - j];
if (c >= 'a')x[j] = c - 'a' + 10;
else x[j] = c - '0';
cnt += __builtin_popcount(x[j]);
}
if (cnt == 5 * m)return 1;
int add = 1;
rep(i, 1, m) {
x[i] += add;
add = x[i] / 32;
x[i] %= 32;
}
return 0;
}
void read(vector<vector<int>>& a, int i) {
string s;cin >> s;
rep(j, 1, m) {
char c = s[m - j];
if (c >= 'a')a[i][j] = c - 'a' + 10;
else a[i][j] = c - '0';
}
}
void chmax(int& x, int y) {
x = max(x, y);
}
void chmin(int& x, int y) {
x = min(x, y);
}
void see2(int x) {
vector<int>res(5,0);
int pos = 0;
while (x) {
res[pos++] = (x & 1);
x >>= 1;
}
per(i, 5 - 1, 0)cout << res[i];
}
void eachT() {
cin >> n >> m;
vector<vector<int>>a(n + 1, vector<int>(m + 1, 0));
vector<int>x(m + 1);
bool flag=readx(x);
rep(i, 1, n)read(a, i);
if (flag) { cout << n << '\n'; return; }
vector<int>l(n + 1), pre(n + 1, 0), dp(n + 1, 0), ma(n + 1, 0);
rep(i, 1, n)l[i] = i;
int ans = -1;
per(p, m, 1) {
per(pos, 4, 0) {
rep(j, 1, n) {
bool bit = (a[j][p] >> pos) & 1;
pre[j] = bit ? pre[j-1] : j;
}
bool xbit = (x[p] >> pos) & 1;
if (xbit) {
rep(j, 1, n) {
int cur = min(l[j], pre[j]);
dp[j] = cur ? ma[cur - 1] + 1 : -1;
ma[j] = max(ma[j - 1], dp[j]);
}
chmax(ans, dp[n]);
} else {
rep(j, 1, n)chmin(l[j], pre[j]);
}
}
}
cout << ans << '\n';
}
signed main() {
ios::sync_with_stdio(false);
cin.tie(0), cout.tie(0);
ll t = 1;
cin >> t;
while (t--) {
eachT();
}
}
2025杭电多校第十场 Cut Check Bit、Multiple and Factor 个人题解的更多相关文章
- 杭电多校第十场 hdu6432 Cyclic 打表找规律
Cyclic Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)Total Su ...
- 杭电多校第十场 hdu6435 CSGO 二进制枚举子集
CSGO Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Others)Total Subm ...
- 杭电多校第十场 hdu6434 Count 欧拉函数打表 快速打表模板
Problem I. Count Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 524288/524288 K (Java/Other ...
- Make Rounddog Happy(2019年杭电多校第十场1011+HDU6701+启发式分治)
目录 题目链接 题意 思路 代码 题目链接 传送门 题意 求有多少个子区间满足\(a_l,a_{l+1},\dots,a_r\)均不相同且\(max(a_l,a_{l+1},\dots,a_r)-(r ...
- [2019杭电多校第十场][hdu6701]Make Rounddog Happy
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=6701 题目大意为求满足 $max(a_{l},a_{l+1}\cdot \cdot \cdot a_{ ...
- 可持久化线段树的学习(区间第k大和查询历史版本的数据)(杭电多校赛第二场1011)
以前我们学习了线段树可以知道,线段树的每一个节点都储存的是一段区间,所以线段树可以做简单的区间查询,更改等简单的操作. 而后面再做有些题目,就可能会碰到一种回退的操作.这里的回退是指回到未做各种操作之 ...
- 2018杭电多校第三场1003(状态压缩DP)
#include<bits/stdc++.h>using namespace std;const int mod =1e9+7;int dp[1<<10];int cnt[1& ...
- HDU 5745 La Vie en rose (DP||模拟) 2016杭电多校联合第二场
题目:传送门. 这是一道阅读理解题,正解是DP,实际上模拟就能做.pij+1 指的是 (pij)+1不是 pi(j+1),判断能否交换输出即可. #include <iostream> # ...
- HDU 5744 Keep On Movin (贪心) 2016杭电多校联合第二场
题目:传送门. 如果每个字符出现次数都是偶数, 那么答案显然就是所有数的和. 对于奇数部分, 显然需要把其他字符均匀分配给这写奇数字符. 随便计算下就好了. #include <iostream ...
- HDU 5742 It's All In The Mind (贪心) 2016杭电多校联合第二场
题目:传送门. 题意:求题目中的公式的最大值,且满足题目中的三个条件. 题解:前两个数越大越好. #include <iostream> #include <algorithm> ...
随机推荐
- C#代码事件
C#代码事件 从今天开始,WPF 的学习将上升到一个新的高度.之前主要都是围绕着界面上的内容,今天了解 C# 代码,让界面真正意义上能够有功能. 本文同时为b站WPF课程的笔记,相关示例代码 上节课自 ...
- Spring扩展接口-BeanFactoryAware
.markdown-body { line-height: 1.75; font-weight: 400; font-size: 16px; overflow-x: hidden; color: rg ...
- LLM 输出配置 (LLM output configuration)
1.概述 大型语言模型(LLM)的输出行为可以通过多种配置参数进行精细控制.这些参数共同决定了模型生成文本的质量.风格和多样性.理解这些配置选项及其相互作用对于有效使用LLM至关重要. 2.输出长度 ...
- Maven 打包时,提示 java: 程序包com.sun.xml.internal.ws.fault 不存在 和 javax.crypto 不存在
报错原因: Maven 打包时,不会导入 JDK 内部的依赖( JDK 属于部署的环境,不属于外部的三方依赖: 解决办法: 在pom.xml 文件中,添加如下 plugin 插件: <plugi ...
- 知名开源项目Alist被收购!惹程序员众怒,开团炮轰甲方
知名开源网盘项目 Alist 被黑产收购?涉及泄露用户隐私?众多开发者成为黑奴?程序员集体炮轰项目评论区? 听起来还挺炸裂的,作为一名程序员,带大家扒一扒这个事件的来龙去脉. 什么是 Alist? A ...
- js调用后台接口下载excel文件
// 下载模板function DownloadExcel() { console.log("进入方法"); const xhr = new XMLHttpRequest(); x ...
- C# 遍历Enum( 枚举)
Type enumType = typeof(Domain.Models.Entitys.PermissionEntity.PermissionTypeEnum); ...
- LoRa产品在智慧畜牧养殖场景的解决方案
案例背景: 在全球农业发展格局中,畜牧业是否成为农业支柱产业,是衡量一个国家农业现代化程度的关键标尺: 科学研究与生产实践表明,环境因素在家禽养殖中起着决定性作用.尤其是在封闭式畜禽舍环境下,光照条件 ...
- Windows链接创建神器:一键生成符号链接与硬链接的智能批处理工具【NuGet】
[自用工具]NuGet 或各项目中共用部分目录或文件,利用DOS符号链接解决重复文件的多份拷贝,起到节省磁盘空间的作用. 告别复杂的命令行操作!这款增强版批处理脚本让Windows链接创建变得简单.直 ...
- maven工具学习
代理源 默认源下载依赖会很慢,在maven的setting.xml的配置下国内镜像源即可 <?xml version="1.0" encoding="UTF-8&q ...