Loading

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)\)

posted @ 2024-01-07 13:39  CoutingStars  阅读(36)  评论(0)    收藏  举报