2019牛客多校第七场 F Energy stones 树状数组+算贡献转化模拟

Energy stones

题意

有n块石头,每块有初始能量E[i],每秒石头会增长能量L[i],石头的能量上限是C[i],现有m次时刻,每次会把[s[i],t[i]]的石头的能量吸干,问最后得到了多少能量?

分析

题意不难理解,模拟题意也不难,但是纯粹模拟会T上天,怎么处理呢?枚举时间不可行,我们可以换个角度思考问题,考虑求每一个石头的贡献行不行?如何求一个石头的贡献呢,只要知道哪个时间点吸了这个石头,就能求出这个石头的贡献了。那时间点如何维护?我们知道,相邻石头的时间点不同只可能是有终点或者起点在相邻的石头中出现,所以时间点可以很好得转移。那么石头贡献怎么算?对于一个石头,如果他的时间段已知,我们记录每个段长度的数量以及时间的总和,对于一个石头,长度可以分成两种情况:设从0开始吸收的能量小于C[i]的时间长度为 d=C[i]/L[i]那么 对于时间段中小于等于d的段来说,只要把这些段的时间乘以该石头的L[i],即可,而对于大于d的段来说,这些段能量都已经叠到上限了,只要把段数乘以C[i]就是这一段的贡献。那么初始有能量怎么办?只要把这一段提出来,特殊处理即可。如果维护小于d的段的数量以及时间?可以使用树状数组(这题卡常,线段树不行),那时间段怎么维护?可以使用set

代码借鉴自咖啡鸡,咖啡鸡??

#include <bits/stdc++.h>
using namespace std;
#define pb push_back
typedef long long ll;
#define int ll
const int maxn=1e5+4;
ll e[maxn],l[maxn],c[maxn],a[maxn<<1],b[maxn<<1];
int n,m;
vector<int>v[maxn];
set<int>s;
int lowbit(int x){
    return x&(-x);
}
ll query_num(int x){
    ll ans=0;
    while(x){
        ans+=a[x];
        x-=lowbit(x);
    }
    return ans;
}
ll query_sum(int x){
    ll ans=0;
    while(x){
        ans+=b[x];
        x-=lowbit(x);
    }
    return ans;
}
void Add(int x,int y){
    while(x<maxn*2){
        if(y>0)a[x]++;
        else a[x]--;
        b[x]+=y;
        x+=lowbit(x);
    }
}
void add(int x){
    if(s.size()==0){
        s.insert(x);
        return ;
    }
    auto p=s.lower_bound(x);
    if(p==s.begin()){
        Add((*p)-x,(*p)-x);
    }
    else if(p==s.end()){
        Add((x-(*prev(p))),(x-(*prev(p))));
    }
    else {
        int tmp1=(*p)-x;
        int tmp2=x-(*prev(p));
        Add(tmp1,tmp1);
        Add(tmp2,tmp2);
        Add(tmp1+tmp2,-(tmp1+tmp2));
    }
    s.insert(x);
}
void del(int x){
    auto p=s.find(x);
    if(s.size()==1){
        s.erase(p);
        return;
    }
    if(p==s.begin()){
        int tmp=(*next(p))-x;
        Add(tmp,-tmp);
        s.erase(p);
    }
    else if(p==prev(s.end())){
        int tmp=x-(*prev(p));
        Add(tmp,-tmp);
        s.erase(p);
    }
    else {
        int tmp1=(*next(p))-x;
        int tmp2=x-(*prev(p));
        Add(tmp1,-tmp1);
        Add(tmp2,-tmp2);
        Add(tmp2+tmp1,tmp2+tmp1);
        s.erase(p);
    }
}
int32_t main(){
    int t;
    scanf("%lld",&t);
    int kase=1;
    while(t--){
        ll ans=0;
        s.clear();
        scanf("%lld",&n);
        memset(a,0,sizeof(a));
        memset(b,0,sizeof(b));
        for(int i=1;i<=n+1;i++)v[i].clear();
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&e[i],&l[i],&c[i]);
        }
        scanf("%lld",&m);
        for(int i=1;i<=m;i++){
            ll x,y,z;
            scanf("%lld%lld%lld",&x,&y,&z);
            v[y].pb(x);
            v[z+1].pb(-x);
        }
        for(int i=1;i<=n;i++){
            for(auto&p:v[i]){
                if(p>0)add(p);
                else del(-p);
            }
            if(!s.size())continue;
            ans+=min(c[i],e[i]+l[i]*(*s.begin()));
            if(l[i]==0)continue;
        ans+=(s.size()-1-query_num(c[i]/l[i]))*c[i]+query_sum(c[i]/l[i])*l[i];//-1是因为把首段特殊处理了
        }
        printf("Case #%lld: %lld\n",kase++,ans);
    }
    return 0;
}

转载于:https://www.cnblogs.com/ttttttttrx/p/11415510.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断路器保护灵敏度验整改及剩余电流监测试点应用站用交流系统断

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值