CF2152D Division Versus Addition 题解
为了方便叙述,我们管先手叫Alice,后手叫Bob。
Alice让数字÷2,Bob让数字+1。
对于这种问题,我们可以从二进制角度来看,Alice就是对一个数右移一位,Bob就是让一个数+1。
那么如果不+1,一个数只需要\(\log(x)\)次操作就可以变为\(1\)。现在考虑加上\(1\)的影响。
- 如果一个数是\(2^k\),那么只有最高位为1,不管Bob怎么做,都是在最低位\(+1\),Alice只需要右移一位就可以消除这个影响,所以说\(2^k\)这种数只需要\(k\)次就可以变为\(1\)。
- 如果一个数是\(2^k+x,2^k>x>1\),那么它必然有一个既不在最高位也不在最低位的位置上有\(1\),只要这一位成为了Alice进行若干次右移之后的最低位,那么Bob只需要不断地末尾\(+1\),就可以一直维持最低位的\(1\),一直维持到二进制为\(11\)的时候,如果再\(+1\),那么就会让位数\(+1\),也就是右移次数\(+1\),所以说这种情况需要\(k+1\)次变为\(1\)。
- 如果一个数是\(2^k+1\),那么如果Alice先操作,就会变为\(2^{k-1}\),只需要\(k-1\)次操作就可以变为\(1\);如果Bob先操作,就会让最终的右移次数变为\(k+1\)。那么Alice和Bob一定要争夺这两个数,显然如果这样的数有\(x\)个,那么Alice能拿到\(\lfloor \frac{x+1}{2} \rfloor\),Bob可以拿到\(x-\lfloor \frac{x+1}{2} \rfloor\)个。
因为上面的第一种情况和第二种情况,Alice和Bob不管怎么操作,另一方都可以执行对应的操作使得得到对应情况的最优解,所以说第一种情况和第二种情况的最优解一定是固定的。第三种情况也是固定的,因为一方希望操作次数尽可能少,另一放希望操作次数尽可能多。
计\(1-i\)位置第一、二、三种情况的数量分别为\(A、B、C\),那么所以说总共的答案就是\(\sum_{i=l}^{r} \lfloor\log(a_i)\rfloor+B_r-B_{l-1}-(\lfloor \frac{B_r-B_{l-1}+1}{2}\rfloor)+C_r-C_l-1\)
代码
#include <bits/stdc++.h>
using namespace std;
/*
*/
#define ll long long
ll sub(int l,int r,vector<ll> &a){
return a[r]-a[l-1];
}
void sol() {
int n,q;
cin>>n>>q;
vector<ll> a(n+1),A(n+1,0),B(n+1,0),C(n+1,0),lg(n+1,0);
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++){
A[i]=A[i-1],B[i]=B[i-1];
lg[i]=lg[i-1]+(ll)floorl(log2(a[i]));
if(__builtin_popcount(a[i])==1) A[i]++;
else if(__builtin_popcount(a[i])==2&&(a[i]&1)) B[i]++;
C[i]=i-A[i]-B[i];
}
while(q--){
int l,r;
cin>>l>>r;
cout<<sub(l,r,lg)+sub(l,r,C)+sub(l,r,B)-(sub(l,r,B)+1)/2<<'\n';
}
}
int main() {
ios::sync_with_stdio(0);
cin.tie(0), cout.tie(0);
int t = 1;
cin >> t;
while (t--) {
sol();
}
return 0;
}
总结:
- 对于只有如/2,+1类的问题,可以从二进制角度考虑
- 与二进制有关的问题可从边界情况入手,如\(2\)的幂次等特殊数值

浙公网安备 33010602011771号