细胞增殖

题目分析

细胞种群数量满足公式 ,其中 为增殖基数, 为稳定基数, 为增殖周期(正整数,)。

给定 条升序排列的观测记录和 组假说 ,对每组假说需要回答:

  1. 有多少条记录能被该假说解释(即存在正整数 使
  2. 所有合法周期值中,单个 对应的最多记录数(增殖峰值)

思路

枚举幂次 + 二分查找

关键观察:对于 增长极快, 最多只有约 个取值。例如 不超过 。因此可以枚举所有可能的 ,计算 ,然后在有序的观测数组中二分查找该值出现的次数。

需要特殊处理:

  • ),所以 。所有值等于 的记录都匹配,且它们对应任意 ,因此总数和峰值均为该值的出现次数。
  • ),所以 。逻辑同上。
  • :从 开始逐步乘以 ,每次用二分统计 在数组中的出现次数,累加总数并更新峰值。当 超过 时停止,防止溢出。

以样例 2 验证: 记录 ,假说

  • ,出现
  • ,出现
  • ,出现
  • ,出现

总数 ,峰值 ,输出 5 2,与期望一致。

复杂度

  • 时间复杂度:,每组假说枚举最多约 个幂次,每次二分
  • 空间复杂度:,存储观测记录数组

代码

#include <bits/stdc++.h>
using namespace std;

int main() {
    int m, q;
    scanf("%d%d", &m, &q);
    vector<long long> rec(m);
    for (int i = 0; i < m; i++) scanf("%lld", &rec[i]);

    while (q--) {
        long long a, b;
        scanf("%lld%lld", &a, &b);

        long long total = 0, peak = 0;

        auto countVal = [&](long long target) -> long long {
            return (long long)(upper_bound(rec.begin(), rec.end(), target) -
                               lower_bound(rec.begin(), rec.end(), target));
        };

        if (a == 0) {
            long long cnt = countVal(b);
            total = cnt;
            peak = cnt;
        } else if (a == 1) {
            long long cnt = countVal(b + 1);
            total = cnt;
            peak = cnt;
        } else {
            // a >= 2,枚举 a^1, a^2, ...
            long long pw = 1;
            for (int n = 1; n <= 62; n++) {
                if (pw > (long long)2e18 / a) break;
                pw *= a;
                long long target = pw + b;
                long long cnt = countVal(target);
                total += cnt;
                peak = max(peak, cnt);
            }
        }

        printf("%lld %lld\n", total, peak);
    }
    return 0;
}
import java.util.*;
import java.io.*;

public class Main {
    public static void main(String[] args) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        StringTokenizer st = new StringTokenizer(br.readLine());
        int m = Integer.parseInt(st.nextToken());
        int q = Integer.parseInt(st.nextToken());

        long[] rec = new long[m];
        st = new StringTokenizer(br.readLine());
        for (int i = 0; i < m; i++) {
            rec[i] = Long.parseLong(st.nextToken());
        }

        StringBuilder sb = new StringBuilder();
        while (q-- > 0) {
            st = new StringTokenizer(br.readLine());
            long a = Long.parseLong(st.nextToken());
            long b = Long.parseLong(st.nextToken());

            long total = 0, peak = 0;

            if (a == 0) {
                long cnt = countVal(rec, b);
                total = cnt;
                peak = cnt;
            } else if (a == 1) {
                long cnt = countVal(rec, b + 1);
                total = cnt;
                peak = cnt;
            } else {
                long pw = 1;
                for (int n = 1; n <= 62; n++) {
                    if (pw > (long) 2e18 / a) break;
                    pw *= a;
                    long target = pw + b;
                    long cnt = countVal(rec, target);
                    total += cnt;
                    peak = Math.max(peak, cnt);
                }
            }

            sb.append(total).append(' ').append(peak).append('\n');
        }
        System.out.print(sb);
    }

    static long countVal(long[] arr, long target) {
        return upperBound(arr, target) - lowerBound(arr, target);
    }

    static int lowerBound(long[] arr, long target) {
        int lo = 0, hi = arr.length;
        while (lo < hi) {
            int mid = (lo + hi) >>> 1;
            if (arr[mid] < target) lo = mid + 1;
            else hi = mid;
        }
        return lo;
    }

    static int upperBound(long[] arr, long target) {
        int lo = 0, hi = arr.length;
        while (lo < hi) {
            int mid = (lo + hi) >>> 1;
            if (arr[mid] <= target) lo = mid + 1;
            else hi = mid;
        }
        return lo;
    }
}
import sys
from bisect import bisect_left, bisect_right

input = sys.stdin.readline

def main():
    m, q = map(int, input().split())
    rec = list(map(int, input().split()))

    out = []
    for _ in range(q):
        a, b = map(int, input().split())
        total = 0
        peak = 0

        if a == 0:
            cnt = bisect_right(rec, b) - bisect_left(rec, b)
            total = cnt
            peak = cnt
        elif a == 1:
            target = b + 1
            cnt = bisect_right(rec, target) - bisect_left(rec, target)
            total = cnt
            peak = cnt
        else:
            pw = 1
            for n in range(1, 63):
                if pw > 2 * 10**18 // a:
                    break
                pw *= a
                target = pw + b
                cnt = bisect_right(rec, target) - bisect_left(rec, target)
                total += cnt
                peak = max(peak, cnt)

        out.append(f"{total} {peak}")

    sys.stdout.write('\n'.join(out) + '\n')

main()
const readline = require('readline');
const rl = readline.createInterface({ input: process.stdin });
const lines = [];
rl.on('line', line => lines.push(line.trim()));
rl.on('close', () => {
    let idx = 0;
    const [m, q] = lines[idx++].split(' ').map(Number);
    const rec = lines[idx++].split(' ').map(BigInt);

    function lowerBound(arr, target) {
        let lo = 0, hi = arr.length;
        while (lo < hi) {
            const mid = (lo + hi) >>> 1;
            if (arr[mid] < target) lo = mid + 1;
            else hi = mid;
        }
        return lo;
    }

    function upperBound(arr, target) {
        let lo = 0, hi = arr.length;
        while (lo < hi) {
            const mid = (lo + hi) >>> 1;
            if (arr[mid] <= target) lo = mid + 1;
            else hi = mid;
        }
        return lo;
    }

    function countVal(target) {
        return upperBound(rec, target) - lowerBound(rec, target);
    }

    const out = [];
    for (let i = 0; i < q; i++) {
        const parts = lines[idx++].split(' ').map(BigInt);
        const a = parts[0], b = parts[1];
        let total = 0, peak = 0;

        if (a === 0n) {
            const cnt = countVal(b);
            total = cnt;
            peak = cnt;
        } else if (a === 1n) {
            const cnt = countVal(b + 1n);
            total = cnt;
            peak = cnt;
        } else {
            let pw = 1n;
            const LIMIT = 2000000000000000000n;
            for (let n = 1; n <= 62; n++) {
                if (pw > LIMIT / a) break;
                pw *= a;
                const target = pw + b;
                const cnt = countVal(target);
                total += cnt;
                peak = Math.max(peak, cnt);
            }
        }

        out.push(`${total} ${peak}`);
    }
    console.log(out.join('\n'));
});