思维题


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


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值