[XSOI Round 1] C.跳跃游戏题解
显然,要获得最大经验值就是枚举所有有经验值的
首先要解决的是如何快速求出
void Init(int n){
lg[1] = 0;
for(int i = 2 ; i <= n ; i++) lg[i] = lg[i >> 1] + 1;
for(int i = 1 ; i <= n ; i++) st[i][0] = a[i];
for(int j = 1 ; j <= lg[n] ; j++){
for(int i = 1 ; i + (1 << j) - 1 <= n ; i++) st[i][j] = __gcd(st[i + (1 << (j - 1))][j - 1] , st[i][j - 1]);
}
}
int Find(int l , int r){
int x = lg[r - l + 1];
return __gcd(st[l][x] , st[r - (1 << x) + 1][x]);
}
接下来就是考虑如何快速算出所有经验值的总和。
这里复杂度是
这里给出的做法是枚举
先给出一个显而易见的定理:若
若求出
题解中只写出
对于这种情况,先对
-
若
x \sim \operatorname{mid} 的\gcd 值正好等于3 ,则记录答案,l=\operatorname{mid} + 1 。 -
若
x \sim \operatorname{mid} 的\gcd 值是3 的倍数且不等于3 ,则l = \operatorname{mid} + 1 。 -
否则,
r = \operatorname{mid} - 1 。
这样子就能求出
其实很多人以为对于
3
6 6 3
其中, 对于
所以说还是要求
最后求出
其中首项为
代码:
#include<bits/stdc++.h>
using namespace std;
int n;
int st[600010][30] , a[600010] , lg[600010];
void Init(int n){
lg[1] = 0;
for(int i = 2 ; i <= n ; i++) lg[i] = lg[i >> 1] + 1;
for(int i = 1 ; i <= n ; i++) st[i][0] = a[i];
for(int j = 1 ; j <= lg[n] ; j++){
for(int i = 1 ; i + (1 << j) - 1 <= n ; i++) st[i][j] = __gcd(st[i + (1 << (j - 1))][j - 1] , st[i][j - 1]);
}
}
int Find(int l , int r){
int x = lg[r - l + 1];
return __gcd(st[l][x] , st[r - (1 << x) + 1][x]);
}
int main(){
scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]);
Init(n);
long long res = 0;
for(int i = 1 ; i <= n ; i++){
int l = 1 , r = (n - i) / 2 + 1;
int best = -1;
while(l <= r){
int mid = (l + r) / 2;
int true_mid = (mid - 1) * 2 + i;
if(Find(i , true_mid) % 3 != 0) r = mid - 1;
else if(Find(i , true_mid) % 3 == 0){
best = max(best , true_mid);
l = mid + 1;
}
else{
l = mid + 1;
}
}
int best2 = 1e9;
if(best != -1){
int l = 1 , r = (best - i) / 2 + 1;
while(l <= r){
int mid = (l + r) / 2;
int true_mid = (mid - 1) * 2 + i;
if(Find(i , true_mid) == 3){
best2 = min(best2 , true_mid);
r = mid - 1;
}
else l = mid + 1;
}
}
if(!(best == -1 || best2 == 1e9)) res += (long long)(best + best2 - 2 * i + 2) * (long long)((best - best2) / 2 + 1) / 2ll;
l = 1 , r = (n - i + 1) / 2;
best = -1;
while(l <= r){
int mid = (l + r) / 2;
int true_mid = mid * 2 + i - 1;
if(Find(i , true_mid) % 2 != 0) r = mid - 1;
else if(Find(i , true_mid) % 2 == 0){
best = max(best , true_mid);
l = mid + 1;
}
else{
l = mid + 1;
}
}
best2 = 1e9;
if(best != -1){
int l = 1 , r = (best - i + 1) / 2;
while(l <= r){
int mid = (l + r) / 2;
int true_mid = mid * 2 + i - 1;
if(Find(i , true_mid) == 2){
best2 = min(best2 , true_mid);
r = mid - 1;
}
else l = mid + 1;
}
}
if(best == -1 || best2 == 1e9) continue;
res += (long long)(best + best2 - 2 * i + 2) * (long long)((best - best2) / 2 + 1) / 2ll;
}
printf("%lld" , res);
return 0;
}
如有错误,请予以指正。
(可能作者脑抽,没想到简单解法,有简单解法可以私信)