2 条题解
-
0
思路
令 。
根据题意发现,随着 增大, 和 都单调不增。
现在在单调函数 上求最接近 的值,容易想到二分 。
指的是这个区间内有多少个矿石重量大于等于 。
指的是这个区间内重量大于等于 的矿石价值之和。
容易发现上面两个式子都可以 预处理出前缀和,并用前缀和 求出。最终做法就是二分 ,每次二分都预处理两个前缀和,并利用前缀和分别求两个式子。
时间复杂度为 。
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; int n,m; ll s,ans=1e18; int w[200003],v[200003]; int l[200003],r[200003]; int cnt[200003]; ll sumv[200003]; ll check(int W){ memset(cnt,0,sizeof(cnt)); memset(sumv,0,sizeof(sumv)); for(int i=1;i<=n;i++){ cnt[i]=cnt[i-1]; sumv[i]=sumv[i-1]; if(w[i]>=W){ cnt[i]++; sumv[i]+=v[i]; } } ll sum=0; for(int i=1;i<=m;i++){ sum+=(cnt[r[i]]-cnt[l[i]-1])*(sumv[r[i]]-sumv[l[i]-1]); } return sum; } int main(){ ios::sync_with_stdio(0); cin.tie(0);cout.tie(0); cin>>n>>m>>s; int lft=0,rig=0,mid; for(int i=1;i<=n;i++){ cin>>w[i]>>v[i]; rig=max(rig,w[i]); } for(int i=1;i<=m;i++){ cin>>l[i]>>r[i]; } while(lft<=rig){ mid=lft+rig>>1; ll y=check(mid); ans=min(ans,abs(s-y)); if(y<s)rig=mid-1; else lft=mid+1; } cout<<ans; return 0; } -
0
#include <iostream> #include <cstring> #include <algorithm> using namespace std; typedef long long LL; const int N=200010; int n,m, w[N],v[N],l[N],r[N]; LL s,sn[N],sv[N],ans=1e18; bool check(int W){ memset(sn,0,sizeof sn); memset(sv,0,sizeof sv); for(int i=1;i<=n;i++){ //前缀和 if(w[i]>=W)sn[i]=sn[i-1]+1,sv[i]=sv[i-1]+v[i]; else sn[i]=sn[i-1],sv[i]=sv[i-1]; } LL y=0; for(int i=1;i<=m;i++) y+=(sn[r[i]]-sn[l[i]-1])*(sv[r[i]]-sv[l[i]-1]); ans=min(ans,llabs(y-s)); //最优解 return y<=s; //W大,y小 } LL find(){ int l=0,r=1e6+1; while(l+1<r){ int mid=l+r>>1; if(check(mid)) r=mid; //最小化 else l=mid; } return ans; } int main(){ scanf("%d %d %lld",&n,&m,&s); for(int i=1;i<=n;i++) scanf("%d%d",&w[i],&v[i]); for(int i=1;i<=m;i++) scanf("%d%d",&l[i],&r[i]); printf("%lld",find()); return 0; }
- 1
信息
- ID
- 67
- 时间
- 1000ms
- 内存
- 128MiB
- 难度
- 6
- 标签
- 递交数
- 32
- 已通过
- 12
- 上传者