CF552C 题解
这篇题解相较于别的题解应该讲的很详细,别的题解一般式子一摆代码一给结束了。如果你感觉这篇题解对你有帮助,请为这篇题解点赞,谢谢!
大意
因为翻译有点烧脑,这里再翻译一遍。
给定 \(w\) 以及 \(m\),其中 \(1\le m\le 10^9\) 且 \(2\le w\le 10^9\),求是否存在一种情况,使得:
\[\sum_{i=0}^kp_iw^i=m
\]
其中 \(p_i\) 的值为 \(1,0,-1\) 的其中一个。
思路
拿到题目大家可能会想到以下两种解法:
- 暴力出奇迹。实际上这么大的数据量奇迹出不了一点。
- 背包,数据量太大了过不了。
而且看这个数据量以及 CF 评测机的能力,能想到复杂度应该在线性或者更低。
接下来讲正解:可以想到,这个求和式里每一项都有至少一个 \(w\),分解因式得:
\[p_0+w(p_1+w(p_2+w(p_3+w(\ldots))))=m
\]
把 \(p_0\) 移走,得:
\[w(p_1+w(p_2+w(p_3+w(\ldots))))=m-p_0
\]
若 \(m-p_0\nmid w\)(也就是说如果 \(m-1\nmid w\) 且 \(m\nmid w\) 且 \(m+1\nmid w\)),结束输出 NO。如果满足,则重设 \(m=\dfrac{m-p_0}{w}\),继续:
\[p_1+w(p_2+w(p_3+w(\ldots))))=m
\]
所以我们就一直把这个式子像剥洋葱一样剥开,遇到剥不开的结束掉。
每一次剥洋葱都能从 \(m\) 中除掉一个 \(w\),如果能剥完也就是一直剥到 \(m=0\),说明是存在题面所述的情况的。这个算法的复杂度是很优秀的,代码如下:
#include <bits/stdc++.h>
#define rty printf("Yes\n");
#define RTY printf("YES\n");
#define rtn printf("No\n");
#define RTN printf("NO\n");
#define rep(v,b,e) for(int v=b;v<=e;v++)
#define repq(v,b,e) for(int v=b;v<e;v++)
#define rrep(v,e,b) for(int v=b;v>=e;v--)
#define rrepq(v,e,b) for(int v=b;v>e;v--)
#define stg string
#define vct vector
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
void solve() {
ll w, m;
cin >> w >> m;
while (m) {
m = (
m % w ? (
(m - 1) % w ? (
(m + 1) % w ? (
-114514 // 哨兵
) : (m + 1) / w
) : (m - 1) / w
) : m / w
);
if (m == -114514) {
RTN
return;
}
}
RTY;
}
main() {
int t; t = 1; while (t--) solve();
return 0;
}
时间复杂度 \(\mathcal{O}(\log_wm)\)。

浙公网安备 33010602011771号