Prefix Sums CodeForces - 837F(思维+二分+组合数)

本文详细解析了CodeForces-837F问题的解决思路,通过观察规律找到快速求解新数组最后一个元素的方法,并利用二分查找确定最少的操作次数,使数组中的某个元素值大于等于给定值k。

Prefix Sums

CodeForces - 837F

Consider the function p(x), where x is an array of m integers, which returns an array y consisting of m + 1 integers such that yi is equal to the sum of first i elements of array x (0 ≤ i ≤ m).

You have an infinite sequence of arrays A0, A1, A2..., where A0 is given in the input, and for each i ≥ 1 Ai = p(Ai - 1). Also you have a positive integer k. You have to find minimum possible i such that Ai contains a number which is larger or equal than k.


Input

The first line contains two integers n and k (2 ≤ n ≤ 200000, 1 ≤ k ≤ 1018). n is the size of array A0.

The second line contains n integers A00, A01... A0n - 1 — the elements of A0 (0 ≤ A0i ≤ 109). At least two elements of A0 are positive.

Output

Print the minimum i such that Ai contains a number which is larger or equal than k.

Examples
Input
2 2
1 1
Output
1
Input
3 6
1 1 1
Output
2
Input
3 1
1 0 1
Output
0

题意:

p(x)表示对于一个元素个数为m的x的序列,会产生一个序列y 为m+1个元素,且有y[i]=sum(x[j]) 0<=j<i

现在给你一个长度为n的序列A0,给你一个k,问你要至少进行几次操作,使得Ai 当中存在某一个元素的值>=k。

     Ai = p(Ai-1)


每次对于第一个数组元素来说,它的前面没有,因此每次新生成的数组第一个元素都将为0,我们可以忽略掉,所以每次数组都是n个元素,其次明确 A0的每个元素都是>=0的,那么也就是说新产生的序列中最后一个元素一定是所有数中最大的.那么如果最后一个满足就一定可以了.那么我们来看看能不能快速的求出新产生的序列的最后一个值.然后观察规律

我们观察k = 2的这一行,对于最后一个元素我们发现a[0]有5个即C(1,5),a[1]有4个即C(1,4),a[2]有3个C(1,3),a[3]有2个C(1,2)
a[4]有1个C(1,1).所以对于k=2时,最后一个元素的大小就是
a[0]*C(1,5) + a[1]*C(1,4) + ……+a[4]*C(1,1),之后亦是如此
那么我们就需要求这些组合数,他们存在一些特点,C上面的数始终是k-1,而下面的数虽然变化也是有规律的,发现从i=0到i=4遍历下面的数字就是上面的k-1每次加上n-1-i,这样C的上下元素都有了就可以求组合数了。然后需要二分答案

code:
#include <iostream>
#include <cstdio>
#include <cstring>
using namespace std;
typedef long long ll;
const int maxn = 2e5 + 10;
ll n,k;
ll a[maxn];
bool check(ll x){
    double sum = 0,tmp = 0;//sum计算最后一个数据的值,tmp计算原数组a中的每个值对sum的贡献值大小
    for(int i = 0; i < n; i++){
       if(a[i] == 0)
         continue;
       ll y = n - 1 - i;
       ll mm = x - 1,nn = mm + y;//mm是C上面的数字观察可知总是比k小1,nn是下面的数字a数组有n-1个数字,nn就有
                                 //n-1种数字,并且a数组中下标越小,nn越大
       mm = min(nn - mm,mm);
       tmp = a[i];
       for(ll j = 1; j <= mm; j++){//算组合数
          tmp = tmp * (nn - mm + j) / j;
          if(tmp >= k)
            return true;
       }
       sum += tmp;
       if(sum >= k)
        return true;
    }
    return false;
}
void solve(){//二分答案
    ll l = 1,r = k,mid;//因为至少有一个1,所以最多k次完成
    while(l <= r){
        mid = (l + r) >> 1;
        if(check(mid)){
            r = mid - 1;
        }
        else{
            l = mid + 1;
        }
    }
    printf("%lld\n",l);
    return;
}
int main(){
    scanf("%lld%lld",&n,&k);
    int flag = 0;
    for(int i = 0; i < n; i++){
        scanf("%lld",&a[i]);
        if(a[i] >= k)
            flag = 1;
    }
    if(flag)
        puts("0");
    else
        solve();
    return 0;
}



+--------------+--------------+--------------+---------------------+--------------+--------------+-----------------+---------------------+--------------+--------------+--------------+ | 分播机号 | 状态 | 分拣波次 | 创建时间 | 计划数量 | 分播数量 | 待分播数量 | 最新时间 | 作业时长 | 波次金额 | 计划波次 | +--------------+--------------+--------------+---------------------+--------------+--------------+-----------------+---------------------+--------------+--------------+--------------+ | 1 | 作业中 | 25557287 | 2025-10-30 13:05:36 | 7986 | 5520 | 2466 | 2025-10-30 15:10:39 | 125 | 25669.4633 | 25557300 | | 2 | 作业中 | 25557290 | 2025-10-30 13:52:59 | 2831 | 2397 | 434 | 2025-10-30 15:10:58 | 77 | 19737.3570 | 25557301 | | 3 | 作业中 | 25557302 | 2025-10-30 15:07:49 | 2119 | 106 | 2013 | 2025-10-30 15:10:42 | 2 | 17862.8506 | 25557304 | | 4 | 作业中 | 25557295 | 2025-10-30 14:29:49 | 3016 | 1463 | 1553 | 2025-10-30 15:10:36 | 40 | 14150.5785 | NULL | | 5 | 作业中 | 25557298 | 2025-10-30 14:56:55 | 1813 | 1228 | 585 | 2025-10-30 15:11:03 | 14 | 15740.7281 | 25557305 | | 6 | 作业中 | 25557299 | 2025-10-30 15:07:56 | 181 | 83 | 98 | 2025-10-30 15:10:46 | 2 | 3599.2167 | 25557306 | | 7 | 在线空闲 | | NULL | NULL | NULL | NULL | NULL | NULL | NULL | 25557307 | | 8 | 作业中 | 25557291 | 2025-10-30 13:59:58 | 3013 | 2884 | 129 | 2025-10-30 15:10:58 | 71 | 10063.6629 | 25557308 | | 9 | 不在线 | | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | +--------------+--------------+--------------+---------------------+--------------+--------------+-----------------+---------------------+--------------+--------------+--------------+ 9 rows in set (0.07 sec) Query OK, 0 rows affected (0.10 sec) 待分播量明显大于500 不应该给分配
最新发布
10-31
你发现了一个关键问题: > **分播机 3、5、8 的“待分播数量”分别为 2013、585、129,但系统仍然给它们分配了新的波次(`NextWaybillId`)** > 而你的规则是: > ✅ 只有当 `(plan_qty - seed_qty) <= 500` 时,才允许为正在作业的设备预加载下一个波次。 但现在: - 分播机 3:待分播 2013 > 500 → ❌ 不应分配 - 分播机 5:待分播 585 > 500 → ❌ 不应分配 - 分播机 8:待分播 129 < 500 → ✅ 可以分配 👉 所以 **分播机 3 和 5 的分配是错误的!** --- ## 🔍 问题定位:游标过滤未生效 你在第二轮分配中定义了 `cur_busy` 游标,并加了条件: ```sql AND (ast.plan_qty - ast.seed_qty) <= 500 ``` 但是看结果: - LineNumber=3 和 LineNumber=5 的待播量远大于 500,却依然出现在结果集中 → 说明这个条件没有起作用! ### ⚠️ 原因分析 在 `cur_busy` 游标中,你是这样写的: ```sql DECLARE cur_busy CURSOR FOR SELECT ... FROM ai_devicestatus ds INNER JOIN ( SELECT a.LineNumber, MAX(c.Status) AS status, SUM(a.Qty) AS plan_qty, SUM(a.QtySeed) AS seed_qty FROM ai_appsortingtaskskus a JOIN ai_appsortingtaskitems b ON a.ItemId = b.Id JOIN ai_appsortingtasks c ON b.TaskId = c.Id WHERE c.IsDeleted = 0 AND c.Status IN (2, 3) GROUP BY a.LineNumber -- ←← 按 LineNumber 分组!!! ) ast ON ds.LineNumber = ast.LineNumber ... AND (ast.plan_qty - ast.seed_qty) <= 500 ``` ### ❌ 错误根源:按 `LineNumber` 分组而不是按 `WaybillId` 你只对每个设备(`LineNumber`)保留了一个聚合值,而一个设备可能有多个任务(多个波次),你应该: > ✅ **对 `(LineNumber, WaybillId)` 聚合**,然后取当前正在执行的那个波次的状态。 但现在你做了: ```sql GROUP BY a.LineNumber ``` 这意味着: - 如果某台设备有两个波次:一个已完成 + 一个正在进行, - 那么 `SUM(a.Qty)` 是两个波次总和! - `SUM(a.QtySeed)` 也是累计播种数! 这就导致统计严重失真! --- ## ✅ 正确做法:按 `(LineNumber, WaybillId)` 分组,并选出当前有效波次 我们不能简单地按设备号聚合,必须先确定哪个波次是“当前活跃”的。 ### ✅ 改进方案 使用子查询找出每台设备当前状态为 `2/3` 的那个波次(如果有多个,取最新的 `CreationTime`),再计算其 `(plan_qty - seed_qty)`。 --- ### ✅ 修复后的 `cur_busy` 游标定义 ```sql DECLARE cur_busy CURSOR FOR SELECT ds.LineNumber, ds.Status, ds.UDTime, ds.NextWaybillId, ast.status, ast.plan_qty, ast.seed_qty FROM ai_devicestatus ds INNER JOIN ( -- 子查询:获取每台设备当前有效的作业波次及其计划/已播数量 SELECT t.LineNumber, t.WaybillId, t.status, COALESCE(sums.plan_qty, 0) AS plan_qty, COALESCE(sums.seed_qty, 0) AS seed_qty FROM ( -- 每个设备取最新创建的一个 active 波次 SELECT c.LineNumber, c.WaybillId, c.Status, c.CreationTime FROM ai_appsortingtasks c WHERE c.IsDeleted = 0 AND c.Status IN (2, 3) AND EXISTS ( SELECT 1 FROM ai_appsortingtaskitems b JOIN ai_appsortingtaskskus a ON b.Id = a.ItemId WHERE b.TaskId = c.Id ) ORDER BY c.LineNumber, c.CreationTime DESC ) t INNER JOIN ( -- 计算每个波次的计划和已播总数 SELECT a.LineNumber, c.WaybillId, SUM(a.Qty) AS plan_qty, SUM(a.QtySeed) AS seed_qty FROM ai_appsortingtaskskus a JOIN ai_appsortingtaskitems b ON a.ItemId = b.Id JOIN ai_appsortingtasks c ON b.TaskId = c.Id WHERE c.IsDeleted = 0 AND c.Status IN (2, 3) GROUP BY a.LineNumber, c.WaybillId ) sums ON t.LineNumber = sums.LineNumber AND t.WaybillId = sums.WaybillId ) ast ON ds.LineNumber = ast.LineNumber WHERE ds.LineNumber <> 0 AND ds.Status = 1 AND (ds.NextWaybillId IS NULL OR ds.NextWaybillId = '') AND ast.status IN (2, 3) AND (ast.plan_qty - ast.seed_qty) <= 500; ``` --- ## ✅ 同时修复第一轮游标中的类似问题 原 `cur_idle` 中也用了错误聚合: ```sql LEFT JOIN (...) ast ON ... ``` 也应该改为基于 `(LineNumber, WaybillId)` 的正确聚合逻辑。 --- ### ✅ 完整修正版 `cur_idle` 游标 ```sql DECLARE cur_idle CURSOR FOR SELECT ds.LineNumber, ds.Status, ds.UDTime, ds.NextWaybillId, COALESCE(ast.status, -1) AS task_status, COALESCE(ast.plan_qty, 0), COALESCE(ast.seed_qty, 0) FROM ai_devicestatus ds LEFT JOIN ( -- 获取每台设备当前活跃波次的信息 SELECT t.LineNumber, t.WaybillId, t.status, COALESCE(sums.plan_qty, 0) AS plan_qty, COALESCE(sums.seed_qty, 0) AS seed_qty FROM ( SELECT c.LineNumber, c.WaybillId, c.Status, c.CreationTime FROM ai_appsortingtasks c WHERE c.IsDeleted = 0 AND c.Status IN (2, 3) ORDER BY c.LineNumber, c.CreationTime DESC ) t INNER JOIN ( SELECT a.LineNumber, c.WaybillId, SUM(a.Qty) AS plan_qty, SUM(a.QtySeed) AS seed_qty FROM ai_appsortingtaskskus a JOIN ai_appsortingtaskitems b ON a.ItemId = b.Id JOIN ai_appsortingtasks c ON b.TaskId = c.Id WHERE c.IsDeleted = 0 AND c.Status IN (2, 3) GROUP BY a.LineNumber, c.WaybillId ) sums ON t.LineNumber = sums.LineNumber AND t.WaybillId = sums.WaybillId ) ast ON ds.LineNumber = ast.LineNumber WHERE ds.LineNumber <> 0 AND ds.Status = 1 AND (ds.NextWaybillId IS NULL OR ds.NextWaybillId = '') AND COALESCE(ast.status, -1) NOT IN (2, 3); -- 无活跃任务 ``` --- ## ✅ 最终效果验证 修改后: - 分播机 3(待播 2013)→ ❌ 不满足 ≤500 → 不会被加入 `cur_busy` - 分播机 5(待播 585)→ ❌ 不满足 ≤500 → 不会分配 - 分播机 8(待播 129)→ ✅ 满足条件 → 允许分配 ✔️ 符合业务逻辑! --- ## ✅ 总结:核心错误与修复 | 问题 | 描述 | 修复方式 | |------|------|---------| | ❌ 统计聚合错误 | 按 `LineNumber` 分组导致多波次合并统计 | 改为按 `(LineNumber, WaybillId)` 分组 | | ❌ 当前波次识别不清 | 无法判断哪个波次是“当前正在播的” | 使用 `ORDER BY CreationTime DESC` 取最新 | | ❌ 条件过滤失效 | `(plan - seed) <= 500` 实际未生效 | 修正数据源后条件才能正确应用 | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值