ABC293解题报告
E. Geometric Progression
题意:求
。 ,不保证质数/互质。
做法一
直接算不好算,但我们可以写出一个递推的形式:设
可以通过矩阵乘法来加速这一过程:
使用矩阵快速幂即可将复杂度做到
By tokusakurai
int main() {
ll A, X, M;
cin >> A >> X >> M;
mint::set_mod(M);
using mat = Matrix<mint>;
mat a(2, 2);
a[0][0] = A;
a[0][1] = 1, a[1][1] = 1;
mat x(1, 2);
x[0][0] = 1;
x *= a.pow(X);
cout << x[0][1] << '\n';
}
做法二
考虑用分治来推式子。假设
于是转化为规模减半的子问题。对于奇数,预先把
还有另一种分治方式。同样假设
对于奇数同样预先处理一位即可。这种做法的好处是代码较短,因为不需要额外乘一些需要同时维护的系数(如第一种分治的
By Nachia
i64 powm(i64 a, i64 x, i64 m){
if(x == 1) return 1 % m;
i64 p = (a + 1) % m;
i64 q = powm(a * a % m, x/2, m);
q = q * p % m;
if(x % 2 == 1) q = (q * a + 1) % m;
return q;
}
int main(){
i64 A, X, M; cin >> A >> X >> M;
cout << powm(A, X, M) << endl;
return 0;
}
做法三
可以根号分块。如果
如果
int A,X,M;
signed main(void) {
//freopen("m.in","r",stdin);
//freopen("m.out","w",stdout);
A=read();X=read()-1;M=read();
int as=1,ans=0;
for(int i=0;i<1000000;i++) {
ans=(ans+as)%M;
as=as*A%M;
}
int st=0,at=1,aq=0;
while(st+1000000-1<=X) {
aq=(aq+ans)%M;
ans=ans*as%M;
at=at*as%M;
st+=1000000;
}
while(st<=X) {
aq=(aq+at)%M;
at=at*A%M;
st++;
}
printf("%lld",aq);
return 0;
}
做法四
显然可以使用等比数列求和公式,得到答案为
对于这种情况,有一个技巧:由于最终答案显然为整数,即 long long
级别,运算需要开 __int128
。
在前十名中,大部分使用的是分治做法,其次是矩阵。Rank1 maspy 使用了此技巧,且用了 Python,则可以免去 __int128
等细节,代码极短。
By maspy
A, X, M = map(int, input().split())
if A == 1:
print(X % M)
else:
mod = M * (A - 1)
x = pow(A, X, mod) - 1
x //= (A - 1)
print(x % M)
F. Zero or One
题意:输入
,判断有多少种进制 ,满足 在 进制下的表示只有 0/1。 。
合法的
于是,我们先枚举
一开始想的是,算出
需要注意的是,由于进制本身已到 long long
级别,在还原表示时还要取幂,所以一定会炸,需要一旦超过
By cxm1024
#define int __int128
void Solve(int test) {
int n = read();
set<int> s;
for (int i = 2; i <= 4000; i++) {
int x = n;
bool flag = 1;
while (x) {
if (x % i > 1) flag = 0;
x /= i;
}
if (flag) s.insert(i);
}
for (int i = 1; i < (1 << 5); i++) {
int l = 2, r = n;
while (l <= r) {
int mid = (l + r) / 2;
int x = 0, now = 1;
for (int j = 0; j < 5; j++, now *= mid) {
if ((i >> j) == 0) break;
if (now > n && (i >> j)) {x = n + 1; break;}
if (i & (1 << j)) {
x += now;
if (x > n) break;
}
}
if (x > n) r = mid - 1;
else if (x < n) l = mid + 1;
else {
s.insert(mid);
break;
}
}
}
cout << s.size() << endl;
}
以上代码在实现上比较麻烦,以下讲几个实现上的技巧。
- 实际上,需要去重只是因为
的界不够紧, 以内存在 位的情况。只要令该分界线恰好为 位的分界线即可。实现中可以枚举 的过程中判断 与 的大小,超过则break
即可。 - 二分还原时,一旦超过
就break
的操作较为繁琐,有一些细节,所以可以直接每一步与 取 来免去这一操作。
By maspy
void solve() {
LL(N);
ll ANS = 0;
// 5 乗を使う
FOR(B, 2, N + 1) {
if (B * B * B * B * B > N) break;
vi F;
ll x = N;
while (x) {
F.eb(x % B);
x /= B;
}
if (MAX(F) <= 1) ++ANS;
}
// 4 乗以下だけを使う
FOR(s, 2, 1 << 5) {
auto f = [&](i128 B) -> i128 {
i128 pow = 1;
i128 val = 0;
FOR(i, 5) {
if (s >> i & 1) { val += pow; }
pow *= B;
chmin(pow, N + 1);
}
chmin(val, N + 1);
return val;
};
ll B = binary_search([&](ll B) -> bool { return f(B) <= N; }, 0, N + 1);
if (1 < B && f(B) == N) { ++ANS; }
}
print(ANS);
}
G. Triple Index
题意:有一个长度为
的数组,每次询问 内 的个数,使 。 。
很难直接处理,于是离线,于是莫队板子。
By cxm1024
#include <bits/stdc++.h>
using namespace std;
#define int long long
int a[200010], t[200010], len = 450;
struct node {
int l, r, num;
} ask[200010];
int ans[200010];
signed main() {
int n, q;
scanf("%lld%lld", &n, &q);
for (int i = 1; i <= n; i++)
scanf("%lld", &a[i]);
for (int i = 1; i <= q; i++) {
scanf("%lld%lld", &ask[i].l, &ask[i].r);
ask[i].num = i;
}
sort(ask + 1, ask + q + 1, [&](node x, node y) {
if (x.l / len != y.l / len)
return x.l / len < y.l / len;
if (x.l / len % 2 == 0) return x.r < y.r;
else return x.r > y.r;
});
int l = 1, r = 0, now = 0;
auto add = [&](int x) {
t[x]++;
if (t[x] >= 3) now += (t[x] - 1) * (t[x] - 2) / 2;
};
auto del = [&](int x) {
t[x]--;
if (t[x] >= 2) now -= t[x] * (t[x] - 1) / 2;
};
for (auto [ll, rr, num] : ask) {
while (r < rr) add(a[++r]);
while (l > ll) add(a[--l]);
while (r > rr) del(a[r--]);
while (l < ll) del(a[l++]);
ans[num] = now;
}
for (int i = 1; i <= q; i++)
printf("%ld\n", ans[i]);
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步