CodeForces451 D.Count Good Substrings
题意:
给一个只含字符a和b的字符串
将字符串处理定义为把连续的相同字符合并,例如:
aabba,处理之后就是aba
aaaa,处理之后就是a
ababa,处理之后就是ababa
问给定的字符串有多少个长度为奇数(偶数)的子串,满足处理之后为回文串。
输出满足条件的奇数串个数和偶数串个数
思路:
因为处理之后连续的相同字符会合并,所以最后串一定是ababab这种形式,
发现只需要左右两端的字符相同,那么一定就是回文串。
因此记录偶数位置和偶数位置中a和b出现的次数,枚举每个位置作为右端点的情况计算即可。
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
signed main(){
string s;
cin>>s;
map<char,int>mark[2];
int ansl=0,ansr=0;
for(int i=0;i<(int)s.size();i++){
ansl+=mark[!(i&1)][s[i]];//偶数
ansr+=mark[(i&1)][s[i]];//奇数
ansr++;//单个字符也是奇数
mark[(i&1)][s[i]]++;
}
cout<<ansl<<' '<<ansr<<endl;
return 0;
}
CodeForces992 D. Nastya and a Game
题意:
给一个长度为n的数组a,和一个整数k
问有多少个子区间[l,r],满足区间积除以区间和等于k,即mul(l,r)/sum(l,r)=k
数据范围:1<=a(i)<=1e8,1<=n<=2e5,1<=k<=1e5
思路:
一个暴力的方法是O(n^2)枚举区间+判断
但是不仅复杂度不够优,而且乘法会爆longlong
这题要从数据范围入手,sum(1,n)*k最大为:1e8乘2e5乘1e5=2e18
而对于a(i)等于1的位置,只影响区间和sum,而不影响区间积mul
所以对于区间积可以跳过a(i)=1的位置,那么每次都至少乘上2,
而2^64>2e18,那么对于一个固定的左端点,除去a(i)=1的位置,最多向右枚举64个位置作为右端点,
那么复杂度就变成O(64n)了,完全可行
那么如何跳过1呢,可以用nt(x)来记录位置x之后的第一个不为1的位置,就能快速跳了,nt数组可以O(n)计算
详见代码
code:
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int maxm=2e5+5;
int sum[maxm];
int nt[maxm];//nt[i]记录i之后第一个不为1的位置
int a[maxm];
signed main(){
int n,k;
cin>>n>>k;
for(int i=1;i<=n;i++){
cin>>a[i];
}
for(int i=1;i<=n;i++){
sum[i]=sum[i-1]+a[i];
}
nt[n]=n+1;
for(int i=n-1;i>=1;i--){
if(a[i+1]==1){
nt[i]=nt[i+1];
}else{
nt[i]=i+1;
}
}
int Max=2e18;
int ans=0;
for(int i=1;i<=n;i++){
int mul=a[i];
int j=i;
while(1){
if(mul%k==0){
int one=nt[j]-1-j;//j之后连续1的个数
int presum=sum[j]-sum[i-1];
int ntsum=sum[j]-sum[i-1]+one;
if(presum<=mul/k&&ntsum>=mul/k)ans++;
}
if(nt[j]>n)break;
j=nt[j];
if(Max/a[j]<mul)break;
mul*=a[j];
}
}
cout<<ans<<endl;
return 0;
}
leetcode 201. 数字范围按位与
题意:
给定L,R,要求计算[L,R]的区间与
思路:
其实就是判断L与R从高位开始有多少位是相同的。
从L到R的过程中高位相同个位置是不会变的,
从高位开始第一个二进制位不同的位置以及之后都是会变的,因此为0。
位运算的基本套路
code:
class Solution {
public:
int rangeBitwiseAnd(int m, int n) {
int ans=0;
while(n!=m){
n>>=1;
m>>=1;
ans++;
}
return (n<<ans);
}
};
CodeForces1217 C. The Number Of Good Substrings
题意:
给一个二进制串,问有多少个子串[l,r]满足[l,r]表示的二进制数等于r-l+1,即等于长度,子串可以有前导零
数据范围:二进制串的长度不超过2e5
思路:
因为二进制串的长度不超过2e5,而2的20次方超过2e5,因此二进制数的长度不超过20位
但是不能直接从1到20枚举子串的长度,因为可以有前导零的存在
反例:27=64,27只有7位,但是加上57个前导零之后也满足条件。
不超过20的是二进制数的长度,而不是串的长度。
因此先枚举第一个1的位置,然后向后拓展最多20位,每次计算还需要多少个前导零,然后判断第一个1的前导零是否足够。
code:
#include<bits/stdc++.h>
using namespace std;
const int maxm=2e5+5;
char s[maxm];
int z[maxm];
int n;
signed main(){
int T;
cin>>T;
while(T--){
scanf("%s",s+1);
n=strlen(s+1);
int len=0;
for(int i=1;i<=n;i++){//计算每个1前面有多少个连续0
if(s[i]=='0')len++;
else{
z[i]=len;
len=0;
}
}
int ans=0;
for(int i=1;i<=n;i++){
if(s[i]=='1'){
int sum=0;
for(int j=i;j<=i+20&&j<=n;j++){
sum=sum*2+s[j]-'0';
int need=sum-(j-i+1);//需要的前导零数量
if(need>z[i])break;
ans++;
}
}
}
cout<<ans<<endl;
}
return 0;
}
CodeForces276 D. Little Girl and Maximum XOR
题意:
给L,R,现在要从[L,R]取出两个数a和b,即满足L<=a<=b<=R,问a异或b的最大值是多少,输出这个最大值
解法:
从高位开始,找L和R第一位个二进制位不相同的位置,假设为第x位,那么答案为(1<<(x+1))-1