题解:P9499 「RiOI-2」change
0.前言:
一道神奇小贪心。题解区的贪心题解感觉都重在讲做法,让人不理解贪的过程,所以写一篇,部分贪心的证明过程可能比较简略,详细可以看其它题解。
注意:本题解的
1.思路:
首先需要一个记录第
举个例子:当前第
接着按编号从小到大枚举物品,对于第
举个例子:第
2.细节:
首先我们可以优化一下对于二元组的排序。显然合成出来的二元组的
接着考虑时间复杂度问题。对于
最后,当合成时遇到了合成的价值大于所有物品的最大值,那么这个合成出来的结果肯定是不好的,直接不要了。并且后面的合成得到的只会更大,直接退出即可。
3.AC code:
#include<bits/stdc++.h>
#define ll long long
#define lll __int128
using namespace std;
const int mod=998244353;
const lll one=1;
ll tid,t,n,v[200005],c[200005],x[200005];
lll st_v[200005],st_c[200005],top,ans;
lll tmp_v[200005],tmp_c[200005],tmp_top;
int main(){
cin>>tid>>t;
while(t--){
scanf("%lld",&n);
for(int i=1;i<=n;i++){
scanf("%lld",&v[i]);
}
for(int i=1;i<=n;i++){
scanf("%lld",&c[i]);
}
for(int i=2;i<=n;i++){
scanf("%lld",&x[i]);
}
top=0;
st_v[++top]=v[1];
st_c[top]=c[1];//将第一个元素的二元组放进去
ans=one*v[1]*c[1];//贡献
for(int i=2;i<=n;i++){
if(x[i]>1){
lll sumv=0,sumc=0;
tmp_top=0;
while(top){
lll vv=st_v[top],cc=st_c[top];
top--;
if(sumc>0){
lll w=min(x[i]-sumc,cc);
sumc+=w;
sumv+=vv*w;
cc-=w;
if(sumv>1e10) break;
if(sumc==x[i]){
tmp_v[++tmp_top]=sumv;
tmp_c[tmp_top]=1;
sumv=sumc=0;
}
else continue;
}
if(vv*x[i]>1e10) break;
tmp_v[++tmp_top]=vv*x[i];//这里某个合成的结果全部来自一个二元组
tmp_c[tmp_top]=cc/x[i];
sumc=cc%x[i];
sumv=sumc*vv;
}
top=0;
for(int j=tmp_top;j>=1;j--){
st_v[++top]=tmp_v[j];//倒着赋值
st_c[top]=tmp_c[j];
}
}
while(top&&st_v[top]<v[i]){
ans-=st_v[top]*st_c[top];//减去原本的,再在下面加更大的回来
c[i]+=st_c[top];//比vi小,变成vi
top--;
}
ans+=one*v[i]*c[i];
st_v[++top]=v[i];
st_c[top]=c[i];//自己这个二元组需要放进去
}
printf("%lld\n",(ll)(ans%mod));
}
return 0;
}