水一份总结 2023.10.8

C. Tea Tasting


题目大意:有n种茶叶和n位品茶师,第i种茶叶剩余a[i]毫升,第i位品茶师一次可以喝b[i]毫升茶。
每一轮,每一位品茶师喝自己对应位置上的茶,即喝min(a[i], b[i])毫升,每一轮结束后,
每位品茶师左移一步,进行下一轮,当所有品茶师都喝不了时结束。
问每一个品茶师可以喝多少茶。

由题意可知,每一位品茶师都可以喝min(b[i], a[i])+min(b[i], a[i - 1])+..min(b[i], a[1])。a[i]都是剩余量。
转化一下:对于每一个a[i],都要被i~n品茶师喝。其中有一位品茶师j会喝不到b[j]毫升,只能喝到a[i]剩下的,但他
前面的品茶师i~j-1都能喝到相应的b[i]~b[j-1]毫升,所以每一位品茶师能够喝到的茶=能喝到的完整的+喝到剩下的总和。
对b前缀和,即对于a[i],我们需要找到sum[j] - sum[i - 1] > a[i],i~j-1能喝到完整的次数加1,最后遍历
计算答案即可。

void solve() {
    int n;
    cin >> n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    vector<ll> sum(n + 1);
    for (int i = 0; i < n; i++) {
        cin >> b[i];
    }
    for (int i = 0; i < n; i++) {
        sum[i + 1] = sum[i] + b[i];
    }
    vector<ll> cnt(n +  1, 0), add(n + 1, 0);  //能喝到多少次完整的, 喝到剩下的总和
    for (int i = 0; i < n; i++) {  //注意:sum下标是从1开始的所以这里的sum[i]等价于原本的sum[i - 1]
        //找到第一个把a[i]吃完的人, sum[j] - sum[i]就是i~j吃的总数
        //sum[j] - sum[i] > a[i] -> sum[j] > a[i] + sum[i]
        int j = upper_bound(sum.begin(), sum.end(), a[i] + sum[i]) - sum.begin() - 1;
        //i~j-1都吃到了完整的b[i]~b[j-1]
        cnt[i]++;  //后面也要用前缀和处理,所以是cnt[i]+1,cnt[j]-1
        cnt[j]--;
        add[j] += a[i] - (sum[j] - sum[i]); //j要加上a[i]剩下的
    }
    for (int i = 0; i < n; i++) {
        cout << cnt[i] * 1LL * b[i] + add[i] << " ";  
        cnt[i + 1] += cnt[i];
    }
    cout << "\n";
}
C. Flexible String


题目大意:给定长度为n的字符串a,b,可以替换k个不同字符。问有多少区间满足a[l,r]==b[l,r]。
(1<=n<=1e5, 0<=k<=10, a中最多有10个不同的字符)
由于a中最多有10个不同的字符,所以我们考虑用二进制暴力枚举所有替换情况。
用双指针计算每一种情况的满足条件的区间个数,i指向当前不满足的,j为上次不满足的,
j+1~i-1就是满足条件的,计算为(i-j-1)*(i-j)/2。

void solve() {
    int n, k; 
    cin >> n >> k;
    string a, b;
    cin >> a >> b;
    int f[26] = { 0 };
    for (char c : a) {
        f[c - 'a']++;
    }
    int cnt = 0;
    for (int i = 0; i < 26; i++) { //计算不同字符的数量,并让每一个字符对应二进制中的每一位
        if(f[i] > 0) f[i] = cnt++;
        else f[i] = -1;
    }
    k = min(k, cnt);
    ll ans = 0;
    for (int mask = 0; mask < (1 << cnt); mask++) {
        //__builtin_popcount(mask) 计算mask中1的个数,也就是可以替换的个数
        //显然能替换的越多最后的值越大,但不能超过k,所以只考虑可替换个数等于k的情况即可
        if(__builtin_popcount(mask) != k) continue;
        ll ret = 0;
        int j = -1;
        for (int i = 0; i <= n; i++) {
            //防止漏掉最后一段 || i位置不相等并且也不能替换, f相当于s[i]在二进制中的位置
            if(i == n || (a[i] != b[i] && !(mask >> f[a[i] - 'a'] & 1))) {
                ret += 1LL * (i - j - 1) * (i - j) / 2;
                j = i;
            }
        }
        ans = max(ans, ret);
    }
    cout << ans << "\n";
}
F. Magic Will Save the World


题目大意:给定w,f每秒增加的水火法术值,n个怪兽的血量,要打败一个怪兽至少消耗与它血量相同的水(火)法术值。
施展魔法不需要时间,问至少需要多少秒能消灭所有怪兽。

消灭一个怪兽,必须使用一种属性的魔法,我们不需要逐步消灭怪兽。
类似于我们把怪兽分成两堆,分别用水,火魔法去攻击。
用背包处理出怪兽能到达的所有血量,然后计算答案取最小值。

void solve() {
    ll w, f;
    cin >> w >> f;
    int n;
    cin >> n;
    vector<int> s(n);
    ll sum = 0;
    for (int i = 0; i < n; i++) {
        cin >> s[i];
       // sum += s[i];
    }

    bitset<N> dp;
    dp[0] = 1;
    for (int i = 0; i < n; i++) {  //预处理出怪兽所有能到达的血量
        dp |= dp << s[i];
        sum += s[i];
    }

    ll ans = 1e9;
    for (int i = 0; i <= sum; i++) {
        if(dp[i]) {
            //(i + w - 1) / w 相当于 i / w(向上取整)
            ans = min(ans, max((i + w - 1) / w, (sum - i + f - 1) / f));
        }
    }
    
    cout << ans << "\n";
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

akb000

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值