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
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;
vectorv[maxn];
sets;
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(x0)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;
}