题解:P17729 [SCP-J 2026] 进位 / carry
xrz_like_genshin · · 题解
P17729 题解
喵!
虽说这道题能用双指针做,可我就偏偏用二分做。
思路
先设
我们发现两个数
移位一下,得:
所以当我们的
题目让我们求
要加一是因为
可以方便的求解。
时间复杂度
上代码
#include <bits/stdc++.h>
#define int long long
#define pc __builtin_popcountll
using namespace std;
const int N = 5e5 + 5, Mod = 998244353;
int t, n, a[N], sum[N], s[N], L[N], R[N], dp[N], dps[N]; // sum 二进制位中一的个数的前缀和,s 原序列的前缀和 dps dp 数组的前缀和
void Solve(){
cin >> n;
for(int i = 1; i <= n; ++i){
cin >> a[i];
sum[i] = sum[i - 1] + pc(a[i]);
s[i] = s[i - 1] + a[i];
}
for(int i = 1; i <= n; ++i){
int l = 1, r = i;
L[i] = n + 1;
R[i] = 0;
while(l <= r){
int mid = (l + r) >> 1;
int x = sum[i] - sum[mid - 1], y = pc(s[i] - s[mid - 1]);
if(x == y){
r = mid - 1;
R[i] = mid - 1;
}else{
l = mid + 1;
}
} // 第一个 C(x,i)=0 的下标减一即为 R_i
r = R[i], l = 1;
while(l <= r){
int mid = (l + r) >> 1;
int x = sum[i] - sum[mid - 1], y = pc(s[i] - s[mid - 1]);
if(x == y + 1){
r = mid - 1;
L[i] = mid;
}else{
l = mid + 1;
}
} // 第一个 C(X,i)=1 的下标即为 L_i
}
for(int i = 1; i <= n; ++i){
if(L[i] > R[i]){
dp[i] = 1; // i 只能当开头
}else{
dp[i] = (1 + dps[R[i]] - dps[L[i] - 1] + Mod) % Mod;
}
dps[i] = (dps[i - 1] + dp[i]) % Mod;
}
cout << dps[n] << '\n';
}
signed main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> t;
while(t--){
Solve();
}
return 0;
}
管理审核辛苦了!