站外题求条!
  • 板块灌水区
  • 楼主void_sans
  • 当前回复0
  • 已保存回复0
  • 发布时间2024/12/14 18:32
  • 上次更新2024/12/14 20:58:20
查看原帖
站外题求条!
1558094
void_sans楼主2024/12/14 18:32

有n根木棍,第i根木棍的长度为Li,n根木棍依次连接到了一起,总共有n-1个连接处.现在允许你最多砍断m个连接处,砍完后n根木棍被分成了很多段,要求满足总长度最大的一段长度最小,求此长度以及满足此长度下的分割方案总数,并将方案结果mod 10007。

(2≤n≤50000, 0≤m≤min(n-1,1000),1≤Li≤1000,内存限制为128MB)

输入: 第1行是两个整数n,m,分别表示木棍的数量和允许砍断连接处的数量。

第2行是n个正整数a1, a2, ..., an,其中ai表示第i跟木棍的长度。

输出: 一行两个空格间隔的整数,即将n根木棍分成多段后,满足总长度最大的一段长度最小,输出此长度以及满足此长度的所有分割方案总数mod 10007的结果。

输入样例1: 3 2 1 1 10

输出样例1: 10 2

输入样例2: 5 2 1 2 3 4 5

输出样例2: 6 1

用时/内存: 1000MS/128MB

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

const int MOD = 10007;
int n, m, a[50005], ans1, ans2, now;
int sum1[50005], sum2[50005], lef[50005], f[50005];

bool check(int x) {
    int len = 0, num = 1;
    for (int i = 1; i <= n; i++) { 
        if (a[i] > x) return false; // 如果单根木棍超过 x,直接返回 false
        len += a[i]; 
        if (len > x) {
            num++; // 需要增加一段
            len = a[i]; // 当前段重新开始
        }
    }
    return num <= m; // 判断切割段数是否超出 m
}

int main() {
    cin >> n >> m;
    m++; // 允许的切割次数加1
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }

    int l = 1, r = 50000000; // 二分搜索的范围
    while (l <= r) {
        int mid = (l + r) / 2;
        if (check(mid)) {
            ans1 = mid; // 记录当前可行的最大值
            r = mid - 1; // 尝试更大的长度
        } else {
            l = mid + 1; // 尝试更小的长度
        }
    }
    
    cout << ans1 << " "; // 输出最大的一段长度

    // 计算分割方案数
    for (int i = 1; i <= n; i++) {
        sum1[i] = sum1[i - 1] + a[i];
        while (sum1[i] - sum1[now] > ans1)
            now++;
        lef[i] = now; // 记录第i根木棍的前一段的最左端点
    }
    for (int i = 1; i <= n; i++) {
        if (sum1[i] <= ans1)
            f[i] = 1; // 不分割的方案数
        sum2[i] = (sum2[i - 1] + f[i]) % MOD; // 计算前缀和
    }

    for (int j = 2; j <= m; j++) { // 前 i 根木棍分割为 j 段
        for (int i = 1; i <= n; i++) {
            if (lef[i] > 0)
                f[i] = (sum2[i - 1] - sum2[lef[i] - 1] + MOD) % MOD; // 使用前缀和更新方案数
            else
                f[i] = sum2[i - 1]; // 如果没有可分割的段
        }
        // 更新前缀和
        for (int i = 1; i <= n; i++) {
            sum2[i] = (sum2[i - 1] + f[i]) % MOD; // 更新前缀和
            ans2 = (ans2 + f[n]) % MOD;// 方案数即为最后一个位置的方案数
        }
    }

    
    cout << ans2 << endl; // 输出方案数

    return 0;
}

2024/12/14 18:32
加载中...