聪明的质监员(前缀和、二分答案)
作者:互联网
链接:https://ac.nowcoder.com/acm/problem/16597
来源:牛客网
空间限制:C/C++ 131072K,其他语言262144K
64bit IO Format: %lld
题目描述
输入描述:
第一行包含三个整数 n,m,S,分别表示矿石的个数、区间的个数和标准值。
接下来的n行,每行2个整数,中间用空格隔开,第i+1行表示i号矿石的重量wi和价值vi 。
接下来的m行,表示区间,每行2个整数,中间用空格隔开,第i+n+1行表示区间[Li,Ri]的两个端点Li和Ri。注意:不同区间可能重合或相互重叠。
输出描述:
输出只有一行,包含一个整数,表示所求的最小值。
示例1输入
5 3 15 1 5 2 5 3 5 4 5 5 5 1 5 2 4 3 3
输出
10
说明
当 W选4的时候,三个区间上检验值分别为20、5、0,这批矿产的检验结果为25,此时与标准值S相差最小为10。
备注:
对于 10%的数据,有 1 ≤ n,m ≤ 10;
对于 30%的数据,有 1 ≤ n,m ≤ 500;
对于 50%的数据,有 1 ≤ n,m ≤ 5,000;
对于 70%的数据,有 1 ≤ n,m ≤ 10,000;
对于 100%的数据,有 1 ≤ n,m ≤ 200,000,0 < wi, vi ≤ 106,0 < S ≤ 1012,1 ≤ Li ≤ Ri ≤ n。
解释一下样例:
若可加的物品的重量必须大于等于W(4)
在1-5中只有4,5两个满足条件可加贡献为 2*(5+5)=20;
在2-4中,只有4一个满足,可加贡献为1*5=5;
3-3中,没有可以加的贡献0
故这样当w取4时候,贡献一共为25
这道题既然要求区间的物品数量和区间的价值之和。我们自然就会想到前缀和。
我们可以采用一个sum数组来记录从前缀和,sum[i]表示从1到i之间所有有效数字value的和,如果我们要求l,r之间的有效值,则可以用sum[r]-sum[i-1]
同理采用num数组来记录某个区间段有效物品的个数,num[i]表示从1到i之间有效物品的个数,l,r之间的有效值个数即为num[r]-num[l-1]
故对应一个区间段的V值即为:(num[r]-num[l-1])*(sum[r]-sum[l-1])
1 #include <bits/stdc++.h> 2 typedef long long LL; 3 #define pb push_back 4 #define mst(a) memset(a,0,sizeof(a)) 5 const int INF = 0x3f3f3f3f; 6 const double eps = 1e-8; 7 const int mod = 1e9+7; 8 const int maxn = 2e5+10; 9 using namespace std; 10 11 int n,m; LL s; 12 LL v[maxn], c[maxn]; 13 LL sum[maxn], num[maxn]; 14 vector<pair<int,int> > vt; 15 16 LL judge(int mid) 17 { 18 for(int i=1;i<=n;i++) 19 { 20 LL sumt = c[i]>=mid? v[i]:0; 21 LL numt = c[i]>=mid? 1:0; 22 sum[i] = sum[i-1] + sumt; 23 num[i] = num[i-1] + numt; 24 } 25 LL res = 0; 26 for(int i=0;i<m;i++) 27 res += (num[vt[i].second]-num[vt[i].first-1])*(sum[vt[i].second]-sum[vt[i].first-1]); 28 return res; 29 } 30 31 int main() 32 { 33 #ifdef DEBUG 34 freopen("sample.txt","r",stdin); //freopen("data.out", "w", stdout); 35 #endif 36 37 scanf("%d %d %lld",&n,&m,&s); 38 for(int i=1;i<=n;i++) 39 scanf("%lld %lld",&c[i],&v[i]); 40 for(int i=1;i<=m;i++) 41 { 42 int a,b; 43 scanf("%d %d",&a,&b); 44 vt.push_back(make_pair(a,b)); 45 } 46 int L = 0, R = 1e6; 47 while(L<=R) 48 { 49 int mid = (L+R)>>1; 50 if(judge(mid)>=s) L=mid+1; 51 else R=mid-1; 52 } 53 LL ans = min(abs(judge(L)-s),abs(judge(R)-s)); 54 printf("%lld\n",ans); 55 56 return 0; 57 }
-
标签:二分,10,前缀,int,sum,质监,mid,num,LL 来源: https://www.cnblogs.com/jiamian/p/13234843.html