题目描述
小T
是一名质量监督员,最近负责检验一批矿产的质量。这批矿产共有 \(n\) 个矿石,从 \(1\)到\(n\) 逐一编号,每个矿石都有自己的重量\(w_i\)以及价值\(v_i\) 。检验矿产的流程是:
给定m个区间\(L_i,R_i\)
选出一个参数\(W\);
对于一个区间\([L_i,R_i]\),计算矿石在这个区间上的检验值Y i:
\[ Y_i= ( \sum_j 1)(\sum_j {v_j}),\quad j \in [L_i,R_i] ,\quad w_j \geq W \]
其中,\(j\)是矿石的编号。
这批矿产的检验结果\(Y\) 为各个区间的检验值之和。即:\(Y_1+Y_2...+Y_m\)
若这批矿产的检验结果与所给标准值\(S\) 相差太多,就需要再去检验另一批矿产。小T
不想费时间去检验另一批矿产,所以他想通过调整参数\(W\) 的值,让检验结果尽可能的靠近标准值\(S\),即使得\(S-Y\) 的绝对值最小。请你帮忙求出这个最小值。
输入格式
第一行包含三个整数\(n,m\),分别表示矿石的个数、区间的个数和标准值。
接下来的\(n\)行,每行\(2\)个整数,中间用空格隔开,第\(i+1\)行表示\(i\)号矿石的重量\(w_i\)和价值\(v_i\)。
接下来的\(m\) 行,表示区间,每行\(2\) 个整数,中间用空格隔开,第\(i+n+1\)行表示区间\([L_i,R_i]\)的两个端点\(L_i\) 和\(R_i\)。注意:不同区间可能重合或相互重叠。
输出格式
一个整数,表示所求的最小值。
输入输出样例
输入 #1
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
输出 #1
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 < w_i,v_i≤10^6,0 < S≤10^{12},1 ≤L_i ≤R_i ≤n\)
問題の解
很明显这是一个二分的板子,想都没想就TLE了
感觉这个题目的背景在任何程度上说都很奇怪啊
首先考虑函数\(Y(W_{cur})\),直觉上告诉我们\(Y(W_{cur})\)具有单调性。证明懒得写。 其实是不会写
\[ Y(W_{cur})=\sum_i^m ( ( \sum_j 1) (\sum_j {v_i})),\quad j \in [L_i,R_i] ,\quad w_j \geq W_{cur} \]
接下来就是要找距离\(Y(W_{cur})-S\)零点最近的函数值。因为\(Y(W_{cur})\)是单调的,所以直接上二分。
\(1^{\circ} \quad\)\(Y(W_{cur})-S \geq 0\),\(W_{cur}\)偏小,将其调大;
\(2^{\circ} \quad\)\(Y(W_{cur})-S < 0\), \(W_{cur}\)偏大,将其调小;
↓然后随便打了一个\(Y(W_{cur})\)的函数,就爆炸了。甚至一开始这个暴力写法的下标还写错了,甚至还在这里卡了好久
inline ll FY1(ll curW)
{
ll Y=0,cnt_j=0,sumv_of_range=0;
for(int i=1;i<=m;i++)
{
cnt_j=0;
sumv_of_range=0;
for(int j=L[i];j<=R[i];j++)
{
if(w[j]>=curW)
{
cnt_j++;
sumv_of_range+=v[j];
}
}
Y+=sumv_of_range*cnt_j;
}
return Y;
}
这里优化的想法也不难,两个前缀数组\(pre_n[],pre_v[]\)即可。
但是没办法,每次调用\(Y(W_{cur})\),\(pre_n[],pre_v[]\)都要重新计算。但是速度比原来还是快很多的。
详见代码。
コード:
#include<bits/stdc++.h>
using namespace std;
#define ll long long
inline int read()
{
char c=getchar();int x=0;
for(;!isdigit(c);c=getchar());
for(;isdigit(c);c=getchar())
x=x*10+c-'0';
return x;
}
const int N=200010;
const ll LLINF=0x3f3f3f3f3f3f3f3f;
int v[N],w[N],L[N],R[N];
ll n,m,S;
ll pre_n[N],pre_v[N];
// inline ll FY1(ll curW)
// {
// ll Y=0,cnt_j=0,sumv_of_range=0;
// for(int i=1;i<=m;i++)
// {
// cnt_j=0;
// sumv_of_range=0;
// for(int j=L[i];j<=R[i];j++)
// {
// if(w[j]>=curW)
// {
// cnt_j++;
// sumv_of_range+=v[j];
// }
// }
// Y+=sumv_of_range*cnt_j;
// }
// return Y;
// }
inline ll FY(int curW)
{
ll Y=0;
memset(pre_n,0,sizeof(pre_n));
memset(pre_v,0,sizeof(pre_v));
for(int i=1;i<=n;i++)
{
if(w[i]>=curW)
{
pre_n[i]=pre_n[i-1]+1;
pre_v[i]=pre_v[i-1]+v[i];
}
else
{
pre_n[i]=pre_n[i-1];
pre_v[i]=pre_v[i-1];
}
}
for(int i=1;i<=m;i++)
Y+=(pre_n[R[i]]-pre_n[L[i]-1])*(pre_v[R[i]]-pre_v[L[i]-1]);
return Y;
}
int main()
{
//freopen("C:\\Users\\Administrator\\Downloads\\testdata (2).in","r",stdin);
ll minw=LLINF,maxw=0;
cin>>n>>m>>S;
for(int i=1;i<=n;i++)
{
w[i]=read();
v[i]=read();
if(maxw<w[i])maxw=w[i];
if(minw>w[i])minw=w[i];
}
for(int i=1;i<=m;i++)
{
L[i]=read();
R[i]=read();
}
ll l=minw-1,r=maxw+2,mid,ans=LLINF;
while(l<=r)
{
mid=((l+r)>>1);
ll Y=FY(mid);
if(Y>=S)l=mid+1;
else r=mid-1;
ans=min(ans,llabs(S-Y));
}
printf("%lld",ans);
return 0;
}