ZR 3611. company 题解

company 题解

题目链接

前言

这个题是一个推式子题,本质也不简单,关键在于转化。
转化后的东西可以用线段树直接维护。
要静下心来一点一点推导

正文

  1. 权值 \(w\) 的定义
    先考虑一批同时开放的订单。
    \(x \le y\) 的时候,操作2是包含操作1的
    \(x > y\) 的时候,可以写成

    \[c=qx+r \ ,\ 0 \le r <x \]

    \(q\) 个整块都用操作一完成余数 \(r\) 若不超过 \(y\) 可以与其它余数合并,否则用一次操作1更优。

    定义 \(w(c)\) 表示的是转化后的工作量,我们希望直接把 \(x\) 的影响直接抛下,计算日期的时候用 \(y\) 来计算的话,就需要这个 \(w(x)\) 的转化值。

    具体定义

    \[w(c)= \begin{cases} c \hspace{3.6cm} x \le y \\ y\lfloor\frac{c}{x}\rfloor+\min(c\ mod\ x,y) \ \ x>y \end{cases} \]

    可以简单理解为 \(w(c)\) 转化完的东西就是天数 \(\times y\)

    观察式子,

    • 第一种情况下 \(\lceil\frac{c}{y}\rceil\) 就是所需要的天数。
    • 然后第二种情况,
      • 先看前面这个 \(\lfloor\frac{c}{x}\rfloor\) 就是有多少个块即我们上式中的 \(q\) ,这不就是需要 \(q\) 天吗。所以要 \(\times y\) 让其一会 \(\div y\) 还是原数 \(q\) (天)。
      • 然后再看后面这个 \(\min(c\ mod\ x,y)\) 看余数大于 \(y\) 还是小于 \(y\)
        • 如果小于 \(y\) 意味着可以和其他余数合并,那么就取 \(c\ mod \ x\) 累加
        • 如果大于 \(y\) 意味着一次操作2即便是一天的所有产量全部给他也不够。不如直接用操作一,那么就是一天。所以 \(+y\) 让其在后面 \(\div y\) 的时候 \(=1\) 表示用了一天

    核心就是要感性理解 \(w(c)\) 转化出来的是以 \(y\) 即操作2为标准的东西

    于是我们就可以得到答案
    对于这些同时开放的公司集合 \(S\)

    \[Ans(S)=\lceil\frac{\sum_{i\in S}w(c_i)}{y}\rceil \]

  2. 考虑开放日期
    如果我们现在考虑一个实际的开放日 \(d\) 。那么对于所有满足 \(s_i \ge d\) 的订单都不能在第 \(d\) 天之前生产,所以对于这一天的答案至少为 。

    \[d+\lceil\frac{\sum_{s_i \ge d}w(c_i)}{y}\rceil-1 \]

    减1是为了去掉 \(d\) 这一天,因为已经算过了。
    这个下届是可以同时达到的,也就是要求每一个开放日 \(d\) 都要满足这个条件。

    \[Ans=\max_d(d+\lceil\frac{\sum_{s_i \ge d}w(c_i)}{y}\rceil-1) \]

    考虑对这个式子变形

    \[d+\lceil\frac{\sum_{s_i \ge d}w(c_i)}{y}\rceil-1\\ =\frac{(d-1)\cdot y}{y}+\lceil\frac{\sum_{s_i \ge d}w(c_i)}{y}\rceil\\ =\lceil\frac{y(d-1)+\sum_{s_i \ge d}w(c_i)}{y}\rceil \]

    于是

    \[Ans=\max\Big(\Big\lceil\frac{y(d-1)+\sum_{s_i \ge d}w(c_i)}{y}\Big\rceil\Big) =\Big\lceil\frac{\max_d\big(y(d-1)+\sum_{s_i \ge d}w(c_i)\big)}{y}\Big\rceil \]

  3. SegmentTree 维护
    对日期离散化,所有有可能出现的日期是

    • 初始的 \(N\)\(S_i\)
    • \(Q\) 次修改的 \(s\)

    所以最多有 \(N+Q\) 个不同日期。
    排序去重得到

    \[d_1<d_2<...<d_m \]

    也就是共有 \(m\) 个不同日期

    对于每个离散化后的日期 \(d_j\) ,定义

    \[a_j=\sum_{s_i=d_j}w(c_i) \]

    表示在这一天开放的公司的和

    我们对式子进行整理,

    \[M=\max_d(y(d-1)+\sum_{s_i \ge d}w(c_i))\]

    那么

    \[Ans=\lceil\frac{M}{y}\rceil \]

    这个 \(M\) 是在某一个 \(d\) 取到最大的结果,

    \[M_j=y(d_j-1)+\sum_{s_i\ge d}w(c_i) \]

    再带入我们上面推导过的 \(a_j\)
    所以:

    \[M_j=y(d_j-1)+\sum_{k=j}^{m}a_k \]

    所以我们要求出的答案就只与下式有关

    \[\max_j(M_j) \]

    至此我们的推导已经完全结束。 完结ヾ(❀撒花╹◡╹)ノ~ ✿.。.・:゚¨゚✎・✿.。.✿*゚¨゚✎・✿。
    (bushi 骗你的不会还真有人信了吧,我上面那么大个线段树的标题后面这么多内容还没提你觉的可能结束吗


    考虑线段树怎么去求出这个 \(max\)
    线段树维护两个信息

    • \(sum:\) 区间权值和。
    • \(best:\) 区间内所有点的 \(y(d_j-1)+\sum_{k=j}^{m}a_k\) 的最大值。

    设当前节点 \(p\) ,左右儿子为 \(ls,rs\) 那么 \(pushup\) 就是

    • \(p.sum=ls.sum+rs.sum\)
    • \(p.best=\max(rs.best,ls.best+rs.sum)\)

    非空叶子是 \(best=y(d_j-1)+a_j\)
    空叶子是 \(best=-\inf\)
    每次修改在旧日期处删除权值在新日期出加入权值,更新之多两个叶子,根节点最大值为 \(M\) ,那么 \(Ans=\lceil\frac{M}{y}\rceil\)

code

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

typedef long long ll;
const int N=4e5+10;
const ll inf=4e18;

struct edge{int to,nxt;}e[N];
int head[N],cnt,n,q,m;
ll sum[N],x,y;
ll v[N],a[N][2],qu[N][3];

void adde(int u,int id,ll w){
    e[++cnt]={id,head[u]};
    head[u]=cnt;
    sum[u]+=w;
}

ll w(ll c){
    if(x<=y) return c;
    return y*(c/x)+min(c%x,y);
}

struct Seg{
    #define ls p<<1
    #define rs p<<1|1
    struct node{ll s,b;}tr[N<<2];
    
    void pushup(int p){
        tr[p].s=tr[ls].s+tr[rs].s;
        tr[p].b=max(tr[rs].b,tr[ls].b+tr[rs].s);
    }
    
    void build(int l,int r,int p){
        if(l==r){
            tr[p].s=sum[l];
            tr[p].b=(sum[l]==0)?-inf:y*(v[l]-1)+sum[l];
            return;
        }
        int m=(l+r)>>1;
        build(l,m,ls);
        build(m+1,r,rs);
        pushup(p);
    }
    
    void update(int p,int l,int r,int pos,ll d){
        if(l==r){
            sum[l]+=d;
            tr[p].s=sum[l];
            tr[p].b=(sum[l]==0)?-inf:y*(v[l]-1)+sum[l];
            return;
        }
        int m=(l+r)>>1;
        if(pos<=m) update(ls,l,m,pos,d);
        else update(rs,m+1,r,pos,d);
        pushup(p);
    }
    
    ll get(){
        if(tr[1].b==-inf) return 0;
        return (tr[1].b+y-1)/y;
    }
}seg;

int main(){
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
    cin>>n>>q>>x>>y;
    
    for(int i=1;i<=n;i++){
        cin>>a[i][0]>>a[i][1];
        v[++m]=a[i][1];
    }
    for(int i=1;i<=q;i++){
        cin>>qu[i][0]>>qu[i][1]>>qu[i][2];
        v[++m]=qu[i][2];
    }
    
    sort(v+1,v+m+1);
    m=unique(v+1,v+m+1)-v-1;
    
    for(int i=1;i<=n;i++){
        int p=lower_bound(v+1,v+m+1,a[i][1])-v;
        adde(p,i,w(a[i][0]));
    }
    
    seg.build(1,m,1);
    cout<<seg.get()<<'\n';
    
    for(int i=1;i<=q;i++){
        int p=qu[i][0];
        ll nc=qu[i][1],ns=qu[i][2];
        
        int oi=lower_bound(v+1,v+m+1,a[p][1])-v;
        seg.update(1,1,m,oi,-w(a[p][0]));
        
        a[p][0]=nc;
        a[p][1]=ns;
        
        int ni=lower_bound(v+1,v+m+1,ns)-v;
        seg.update(1,1,m,ni,w(nc));
        
        cout<<seg.get()<<'\n';
    }
    
    return 0;
}
posted @ 2026-08-20 11:22  词印  阅读(6)  评论(0)    收藏  举报