2021牛客第四场

B 武辰延的字符串
1.题目

在这里插入图片描述
在这里插入图片描述

2.题意

求多少个s串的前缀组合能构成t的前缀

3.思路

字符串哈希 + 二分
用哈希存储s的所有前缀,枚举前缀:
第 i+1个 二分查找最大的满足去掉si前缀的剩下的串和s匹配的最大前缀

比如:
s = “abaabc”
t = “ababaabb”

s串和t串的前两个都相同,此时i为2,那么t - “ab” = “abaabb”
开始二分查找最大的s与t的匹配的前缀,可以看出是abaab。

4.代码
#include <iostream>
#include <cstring>

using namespace std;
typedef unsigned long long ULL;
const int P = 131, Q = 100010, N = 100010;
const int mod = 1e9 + 7;
int n1, n2;
ULL sum1[N], sum2[N], p[N]; //存前缀和
char s[N], t[N]; 
ULL get1(int l, int r){
    return sum1[r] - sum1[l - 1]* p[r - l + 1];
}
ULL get2(int l, int r){
    return sum2[r] - sum2[l - 1]* p[r - l + 1];
}
int main(){
    //读入
    scanf("%s", s + 1);
    scanf("%s", t + 1);
    n1 = strlen(s + 1);
    n2 = strlen(t + 1);
    
    //初始化
    sum1[0] = sum2[0] = 0;
    p[0] = 1;
    
    for(int i = 1; i <= n1; ++ i)
        sum1[i] = sum1[i - 1] * P + s[i];
    for(int i = 1; i <= n2; ++ i)
        sum2[i] = sum2[i - 1] * P + t[i], p[i] = p[i - 1] * P;
    
    //计算答案
    ULL ans = 0;
    for(int i = 1; i < n2 ; ++ i){
        if(s[i] != t[i]) break;
        int l = 1, r = n1, tmp = 0;
        while(l <= r){
            int mid = l + r >> 1;
            ULL t1 = get1(1, mid);
            ULL t2 = get2(i + 1, i + mid);
            if(t1 == t2) tmp = mid, l = mid + 1;  
            else r = mid - 1;
        }
        ans += tmp;
    }
    printf("%llu\n", ans);
    return 0;
}
J 邬澄瑶的公约数
题目

在这里插入图片描述
输入

2
9 3
1 2

输出

9

题意:
求gcd(a1^b1, a2 ^b2, a3 ^b3, …, an ^bn)

思路:

  1. 暴力: 由于bi <= 1e4太大, 不好求。
  2. 我们可以把每个ai分解质因数,求每个质因数最小的次数,最后乘起来即可
    在这里插入图片描述

AC代码:

#include <iostream>
#include <cstring>
#include <string>

using namespace std;
typedef long long LL;
const int mod = 1e9 + 7, N = 10005; 
int n;
int a[N], b[N], f[N];
int p[N], cnt;
bool st[N];
void Prime(int n){  //素数筛筛出素数
    for(int i = 2; i <= n; ++ i){
        if(!st[i]) p[cnt ++] = i;
        for(int j = 0; p[j] <= n / i; ++ j){
            st[p[j] * i] = true;
            if( i % p[j] == 0) break;
        }
    }
}
LL qp(LL a, LL b){ //快速幂
    LL res = 1;
    while(b){
        if(b&1) res = res * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return res;
}
int main(){
    Prime(N);
    memset(f, 0x3f, sizeof f); //f:存储最小的次方
    scanf("%d", &n);
    for(int i = 0; i < n; ++ i) scanf("%d", a + i);
    for(int i = 0; i < n; ++ i) scanf("%d", b + i);
    
    for(int i = 0; i < n; ++ i){
        int x = a[i], y = b[i];
        for(int j = 0; j < cnt; ++ j){ //分解质因数
            int c = 0;                 //c保存该质因数的次方数
            if(x % p[j] == 0)
                while(x % p[j] == 0) x /= p[j], c ++;
            c = c * y;                 //因为是x^y, 所以次方要乘bi
            f[j] = min(f[j], c);       //维护最小次方数组
        }
    }
    LL sum = 1;
    for(int i = 0; i < cnt; ++ i)
        sum = (sum * qp(p[i], f[i])) % mod;   //计算结果
    printf("%lld\n", sum);
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值