P2827 蚯蚓 [单调性]

蚯 蚓 蚯蚓

题面见链接 .


正 解 部 分 \color{red}{正解部分}

若直接使用优先队列暴力搞的话, 复杂度为 O ( M l o g ( N + M ) ) O(Mlog(N+M)) O(Mlog(N+M)), 其中 M M M 7 ∗ 1 0 6 7*10^6 7106 .
需要 O ( M ) O(M) O(M) 的时间复杂度才能 A C AC AC .

设 设 蚯蚓 x x x 的长度为 l e n x len_x lenx 被切开后分为 p l e n x , l e n x − p l e n x plen_x, len_x-plen_x plenx,lenxplenx 两个蚯蚓,
蚯蚓 y y y 的长度为 l e n y len_y leny 被切开后分为 p l e n y , l e n y − p l e n y plen_y, len_y-plen_y pleny,lenypleny 两个蚯蚓, 且 l e n y &lt; l e n x len_y &lt; len_x leny<lenx,

假如刚开始切 x x x 得到 p l e n x , ( 1 − p ) l e n x plen_x, (1-p)len_x plenx,(1p)lenx, 然后切 y y y, 得到 p ( l e n y + q ) , ( 1 − p ) ( l e n y + q ) p(len_y+q), (1-p)(len_y+q) p(leny+q),(1p)(leny+q),

此时切 x x x 得到的两个蚯蚓长度分别为

  • p l e n x + q &gt; p ( l e n y + q ) = p l e n y + p q plen_x + q &gt; p(len_y+q)=plen_y+pq plenx+q>p(leny+q)=pleny+pq
  • ( 1 − p ) l e n x + q &gt; ( 1 − p ) ( l e n y + q ) = ( 1 − p ) l e n y + ( 1 − p ) q (1-p)len_x + q &gt; (1-p)(len_y+q)=(1-p)len_y+(1-p)q (1p)lenx+q>(1p)(leny+q)=(1p)leny+(1p)q

得到结论: 较长的蚯蚓先切得到的小蚯蚓 比 较短的蚯蚓后切得到的小蚯蚓 更长., 满足 单调性 .

于是开三个单调的队列, 第一个队列存储开始时排好序的蚯蚓, 第二个存切了之后第一条小蚯蚓, 第三个存第二条小蚯蚓,
每次取出三个队列中的最大队首, 然后 O ( 1 ) O(1) O(1) 处理即可 .

时间复杂度 O ( M ) O(M) O(M) .


实 现 部 分 \color{red}{实现部分}

对长度增长的处理使用外部标记 t a g tag tag, 每次取出元素 x x x 后, 将 x + = t a g x += tag x+=tag, 然后分裂成 x 1 , x 2 x_1, x_2 x1,x2, 推入队列时需要 − = t a g + q -=tag+q =tag+q, 然后 t a g + = q tag += q tag+=q .

#include<bits/stdc++.h>
#define reg register
typedef long long ll;

const int maxn = 1e7 + 5;

int read(){
        char c;
        int s = 0, flag = 1;
        while((c=getchar()) && !isdigit(c))
                if(c == '-'){ flag = -1, c = getchar(); break ; }
        while(isdigit(c)) s = s*10 + c-'0', c = getchar();
        return s * flag;
}

int N;
int M;
int q_;
int u_;
int v_;
int t_;
int A[maxn];

ll tag;
ll Ans[maxn];

int main(){
        scanf("%d%d%d%d%d%d", &N, &M, &q_, &u_, &v_, &t_);
        std::queue <ll> Q[3];
        for(reg int i = 1; i <= N; i ++) A[i] = read();
        std::sort(A+1, A+N+1);
        for(reg int i = N; i >= 1; i --) Q[0].push(A[i]);
        for(reg int i = 1; i <= M; i ++){
                int t = 0;
                if(Q[0].size()) t = 0;
                else if(Q[1].size()) t = 1;
                else t = 2;
                if(Q[0].size() && Q[0].front() > Q[t].front()) t = 0;
                if(Q[1].size() && Q[1].front() > Q[t].front()) t = 1;
                if(Q[2].size() && Q[2].front() > Q[t].front()) t = 2;
                Ans[i] = Q[t].front() + tag;
                ll tmp = Q[t].front() + tag; Q[t].pop();
                ll tmp_1 = 1ll*tmp*u_ / v_;
                ll tmp_2 = tmp - tmp_1;
                Q[1].push(tmp_1-tag-q_), Q[2].push(tmp_2-tag-q_);
                tag += q_;
        }
        for(reg int i = t_; i <= M; i += t_) printf("%lld ", Ans[i]);
        printf("\n");
        int cnt = 0;
        for(reg int i = 0; i <= 2; i ++) while(!Q[i].empty()){ Ans[++ cnt] = Q[i].front(); Q[i].pop(); }
        std::sort(Ans+1, Ans+cnt+1); std::reverse(Ans+1, Ans+cnt+1);
        for(reg int i = t_; i <= N+M; i += t_) printf("%lld ", Ans[i]+tag);
        return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值