[ABC322G] Two Kinds of Base
第一次赛后马上AK ABC,好激动,感觉是这场太水了,一看评分,G有2800?!
感觉这个 Trick 挺有用的:某些变量真正能取到的值其实远远没有给的范围那么大,除了某些特殊情况,而这些特殊情况可以用特殊的方式统计答案。
题意
对于一个非负整数序列 \(S=(S_1,S_2,\dots,S_k)\) 和一个整数 \(a\),定义函数 \(f(S,a)\) 如下:
给定正整数 \(N\) 和 \(X\)。求满足以下所有条件的非负整数序列 \(S=(S_1,S_2,\dots,S_k)\) 和正整数 \(a\) 及 \(b\) 的三元组 \((S,a,b)\) 的数量模 \(998244353\)。
- \(k \ge 1\)
- \(a,b \le N\)
- \(S_1 \neq 0\)
- \(S_i < \min(10,a,b)(1 \le i \le k)\)
- \(f(S,a) - f(S,b) = X\)
思路
首先,可以把数列 \(S\) 分别看做是 \(k\) 位 \(a\) 进制数和 \(k\) 位 \(b\) 进制数每一位上的值,记这两个数分别记作 \(A\) 和 \(B\)。题目中的最后一个限制就等价于 \(A-B=X\),其中 \(X\) 是十进制意义下的。
显然 \(f(S,a) - f(S,b) = \sum_{i=1}^{k} S_i \times (a^{k - i}-b^{k - i})\),由于 \(X\) 为正整数,故 \(a > b\)。
所以上式的这一部分 \(a^{k - i}-b^{k - i}\) 随 \(i\) 增大单调递减,当 \(i=k\) 时,\(a^{0}-b^{0} = 0\),即 \(S_k\) 的取值不会对 \(A-B\) 的值产生影响。
考虑最朴素的做法,枚举 \(k,a,b\),并构造一种合理的 \(S\) 使得满足上述限制。可以证明,如果存在这样的一个 \(S\),那么 \((S_1,S_2,\dots,S_{k-1})\) 是唯一的。
证明:假设从 \(i=1\) 开始依次填 \(S_i\),设 \(X'\) 表示 \(X\) 减去 \(S_1\) 至 \(S_{i-1}\) 的总贡献后的值, \(S_{i+1}\) 到 \(S_k\) 能产生的最大贡献为 \(C\),\(S_i=1\) 时仅 \(S_i\) 产生的贡献为 \(D\)。能得到:\(D=a^{k-i}-b^{k-i}=(a-1)\times\sum_{j=i+1}^{k} a^{k-j}-(b-1)\times\sum_{j=i+1}^{k} b^{k-j} > (b-1)\times(\sum_{j=i+1}^{k} a^{k-j}-\times\sum_{j=i+1}^{k} b^{k-j})\)。所以,当 \(X'>C\) 时,只有 $S_i= \left \lfloor \frac{X'}{a^{k-i} - b^{k-i}}\right \rfloor $ 时才有可能满足条件。而当 \(X'\le C\) 时,\(S_i\) 只能等于 \(0\)。如此贪心地往下填 \(S\) 的每一位,一直到 \(S_{k-1}\),能确定一组唯一的 \((S_1,S_2,\dots,S_{k-1})\)。
这样在固定 \(k,a,b\) 的情况下可以 \(O(k)\) 地统计答案,\(k\) 的范围是 \(O(\log X)\) 级别,但 \(a,b\) 的范围却是与 \(n\) 同阶的。
其实有效的 \(a,b\) 远没有这么多。
由于 \(S_1 \neq 0\),这等价于 \(a^{k-1}-b^{k-1} \le X\),可以发现,当 \(k \ge 3\) 时,满足这个条件的二元组 \((a,b)\) 是很少的(当 \(N=10^9,X=2\times 10^5\) 时,只有 \(620787\) 组),每一次可以用 \(O(\log X)\) 的时间统计答案。
而当 \(k = 2\) 时,\(X = (a-b)\times S_1\),用 \(O(\sqrt x)\) 的时间统计答案即可。
Code
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define ull unsigned long long
#define l(x) x<<1
#define r(x) x<<1|1
#define mpr make_pair
//mt19937_64 ra(time(0) ^ (*new char));
const ll SIZE = 200005;
const ll mod = 998244353;
ll n, X, ans;
inline ll rd(){
ll f = 1, x = 0;
char ch = getchar();
while(ch < '0' || ch > '9'){
if(ch == '-') f = -1;
ch = getchar();
}
while(ch >= '0' && ch <= '9'){
x = (x << 1) + (x << 3) + (ch ^ 48);
ch = getchar();
}
return f*x;
}
ll power(ll x, ll y){
ll jl = 1;
while(y){
if(y & 1) jl = (jl * x);
x = (x * x);
y >>= 1;
}
return jl;
}
int main(){
n = rd(), X = rd(); ll cnt = 0;
for(ll i = 1; i*i <= X; i++){
if(X%i == 0){
if(i < 10){
for(ll a = i+1+(X/i); a <= n; a++){
ll b = a-(X/i);
if(b >= 10) break;
ans = (ans + min(10ll, b)) % mod;
}
if(n-(X/i) >= 10){
ans = (ans + (10 * (n-(X/i)-10+1)) % mod) % mod;
}
}
if(i != (X/i) && (X/i) < 10){
for(ll a = (X/i)+1+i; a <= n; a++){
ll b = a-i;
if(b >= 10) break;
ans = (ans + min(10ll, b)) % mod;
}
if(n-i >= 10){
ans = (ans + (10 * (n-i-10+1)) % mod) % mod;
}
}
}
}
for(ll k = 3; k <= 20; k++){
for(ll a = 1; a <= n; a++){
if(power(a, k-1)-power(a-1, k-1) > X) break;
for(ll b = a-1; b >= 1; b--){
if(power(a, k-1)-power(b, k-1) > X) break;
ll jl = X; bool flag = 1;
for(ll i = 1; i < k; i++){
if(jl / (power(a, k-i)-power(b, k-i)) >= min(10ll, min(a, b))) flag = 0;
jl = (jl % (power(a, k-i)-power(b, k-i)));
}
if(jl == 0 && flag){
ans = (ans + min(10ll, min(a, b))) % mod;
}
}
}
}
printf("%lld", ans);
return 0;
}