首先我们来看看观察每个区间的值
就以题目中的示例为例子解释一下
对应区间段1-5
由于可加的物品的重量必须大于等于W(4)
故在1-5中只有4,5两个可加贡献为 25+25=20;2是因为有两个数符号条件
在2-4中,只有4一个可加贡献为1*5=5;
3-3中,没有可以加的贡献0
故这样当w取4时候,贡献一共为25
这样我们可以看出这道题主要是求出某个区域的的值,这样我们便采用前缀和
我们可以采用一个sum数组来记录从前缀和,sum[i]表示从1到i之间所有有效数字的和
如果我们要求l,r之间的有效值,则可以用sum[r]-sum[i-1];
同理采用cnt数组来记录某个区间段有效物品的个数,cnt[i]表示从1到i之间有效物品的个数,
l,r之间的有效值个数即为cnt[r]-cnt[l-1];
故对应一个区间段的V值即为(cnt[r]-cnt[l-1])*(sum[r]-sum[l-1]);
#include<bits/stdc++.h> using namespace std; long long n,m,s; long long sum[1000005];//用于前缀和存储 long long weight[1000005];//存储重量 long long value[1000005];//存储价值 int lefti[1000005];//存储区段的左结点 int righti[1000005];//存储区段右结点 int cnt[1000005];//存储有效数组的前缀 long long getsum(int w)//求出W=w时候,全部区域的V值和 { long long ans=0; for(int i=1;i<=n;i++)//先求出 { if(weight[i]>=w)//如果满足条件 { sum[i]=sum[i-1]+value[i];//加上这个物品的价值 cnt[i]=cnt[i-1]+1;//有效值加1 } else { sum[i]=sum[i-1]; cnt[i]=cnt[i-1]; } } for(int i=1;i<=m;i++) { ans+=(cnt[righti[i]]-cnt[lefti[i]-1])*(sum[righti[i]]-sum[lefti[i]-1]); } return ans; } int main() { cnt[0]=0; sum[0]=0; int maxsize=-1;//用于记录最大值,便于后面确定二分法的起点 scanf("%lld%lld%lld",&n,&m,&s); for(int i=1;i<=n;i++) { scanf("%lld %lld",&weight[i],&value[i]); if(weight[i]>maxsize) maxsize=weight[i]; } for(int i=1;i<=m;i++) { scanf("%d %d",&lefti[i],&righti[i]); } int l=0,r=maxsize+1; int mid=(l+r+1)>>1; int vis=0;//判断是否有差为0情况 while(l<r) { if(getsum(mid)>s)因为大于标准,则说明w取小了让很多物体都符合条件,故要增加w { l=mid; } else if(getsum(mid)<s) { r=mid-1; } else { printf("0"); vis=1; break; } mid=(l+r+1)>>1; } if(!vis) printf("%lld",min(abs(s-getsum(r+1)),abs(getsum(r)-s))); }
最后祝大家天天AC,坚持下去!