其他分享
首页 > 其他分享> > 聪明的质监员(前缀和、二分答案)

聪明的质监员(前缀和、二分答案)

作者:互联网

 

链接:https://ac.nowcoder.com/acm/problem/16597
来源:牛客网


时间限制:C/C++ 1秒,其他语言2秒
空间限制: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