Codeforces Round #512 (Div. 2, based on Technocup 2019 Elimination Round 1) E. Vasya and Good Sequences
题目链接
官网题解写的好清楚,和昨晚Aguin说的一模一样……
这题只和每个数1的个数有关,设每个数1的个数的数组为$b$,就是首先一段如果是好的,要满足两个条件:
1.这一段$b$数组和为偶数,因为奇数总会多出一个1,消不掉。
2.这一段$b$数组中最大的要小于等于这一段总和的一半,因为自己里面的1和自己不能消。
有了这两个条件,先处理第一个条件,做法是枚举左端点,然后统计合法的右端点的个数,就$odd$和$even$数组统计后缀和为奇还是偶,偶-偶,奇-奇就好了,这样能够保证$[l,r]$区间是偶数。
再处理第二个条件,因为每个数$<=10^{18}$,所以最多有60个1,而且每个数不为$0$,所以每个数最少有一个1,那$[l,r]$区间长度大于60后,$b$数组的最大值一定小于等于总和的一半,所以只要把$r$在$[l,l+60]$中,和为偶数最大值大于总和一半的减去就行了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 typedef long long ll; 4 const int maxn = 3e5 + 50; 5 int b[maxn]; 6 int sum[maxn]; 7 int odd[maxn], even[maxn]; 8 int main() 9 { 10 int n; scanf("%d", &n); 11 for(int i = 1; i <= n; i++) 12 { 13 ll x; 14 scanf("%lld", &x); 15 while(x) 16 { 17 if(x % 2LL == 1LL) b[i]++; 18 x >>= 1LL; 19 } 20 //printf("%d\n", b[i]); 21 } 22 for(int i = n; i >= 1; i--) 23 { 24 sum[i] = sum[i + 1] + b[i]; 25 odd[i] = odd[i + 1], even[i] = even[i + 1]; 26 if(sum[i] % 2 == 0) even[i]++; 27 else odd[i]++; 28 } 29 ll ans = 0; 30 for(int i = 0; i <= n; i++) ///枚举左端点 31 { 32 if(sum[i] % 2 == 0) ans += even[i + 1]; 33 else ans += odd[i + 1]; 34 int mx = 0; 35 if(i == 0) continue; ///i=0,统计的是右端点为n的区间 36 for(int j = i; j <= min(n, i + 65); j++) 37 { 38 mx = max(mx, b[j]); 39 int tmp = sum[i] - sum[j + 1]; 40 if((tmp % 2 == 0) && (mx + mx) > tmp) ans--; 41 } 42 } 43 printf("%lld\n", ans); 44 return 0; 45 }