学习内容 Day_3

Posted by n0000000000o's Blog on July 20, 2018

P1314 聪明的质监员

分析:

对于每一个 $Y_i$ ,有
\(Y_i = \sum\limits _{j} \times \sum\limits_jv_j , j \in [L_i, R_i] 且 w_j \geq W ,j为矿石编号\)
即区间 $[L_i ,R_i]$ 中所有 $w_j\geq W$ 的石块都选, 在区间 $[L_i ,R_i]$ 内
$Y_i = 满足条件石头个数 * 满足条件石头价值之和$
\(Y = Y_1 + Y_2 + ... + Y_i\)
即求 $abs(s - Y)$ 的最小值
考虑二分查找枚举答案 $w$
通过 $Y(w) - s$ 判断二分的区间
编写 $Y(w)$ 时,因为存在多次区间询问,考虑用前缀和降低复杂度

代码实现

#include <bits/stdc++.h>
using namespace std;
/*单调性说明: 
	W上升 ,  每一个 Yi 中 两项都下降, Yi下降,   所以总的 Y 也一定下降 ,单调下降(其实是不上升) 
*/
int erfen(int l, int r);
long long Y (int x);
long long findnum(int l, int r);
long long findsum(int l, int r);
long long n, m, s;
long long numm[200000 + 9], summ[200000 + 9], v[200000 + 9], w[200000 + 9];//numm[] summ[] 记录区间i j 大于W的个数以及和  前缀和 
int ll[200000 + 9], rr[200000 + 9];
long long read();
int main()
{
	long long maxw = 0;
	cin >> n >> m >> s;
	for (int i = 1; i <= n; i++)
	{
		w[i]=read();
		v[i]=read();
		maxw = max(maxw, w[i]);//记录下最大重量以此为二分上边界 
	}
	for (int i = 1; i <= m; i++)
	{
		ll[i] = read();
		rr[i] = read();
	}
	int w = erfen(0, maxw);
	cout << min ( Y(w) - s, s - Y(w+1) );//见下    ↓↓↓↓↓↓ 
	return 0;
} 
long long read()//快读 
{
	long long x=0;
	char c=getchar();
	while(c>'9'||c<'0') 
		c=getchar();
	while(c<='9'&&c>='0') 
	{
		x=x*10+c-'0';
		c=getchar();
	}
	return x;
}
int erfen (int l, int r)//找使  Y-S >=0 的最接近的W 此时 的答案在 W与 W+1 之间 (因为要求绝对值) 
{
	if (l == r)
		return l;
	int mid = ((l+r+1)>>1);
	if (Y(mid) >= s) 
		return erfen (mid, r);
	else
		return erfen (l ,mid - 1); 
}
long long Y (int x)
{
	long long y = 0;
	for (int i = 1; i <= n; i++)//初始化 
	{	
		numm[i] = numm[i-1] ;
		summ[i] = summ[i-1] ;
		if(w[i] >= x)	//满足条件 ,前缀和增加 
		{	
			numm[i] += 1;
			summ[i] += v[i];
		}	
	}
	for (int i = 1; i <= m; i++)
	{
		int l = ll[i];
		int r = rr[i];
		y += (numm[r] - numm[l-1])* (summ[r] - summ[l-1]);
	}
	return y;
}