Luogu P1314 聰明的質監員
P1314 聰明的質監員
難度 提高+/省選-
時空限制 1000ms / 128MB
題目描述
小T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n 個礦石,從 1到n 逐一編號,每個礦石都有自己的重量 wi 以及價值vi 。檢驗礦產的流程是:
1 、給定m 個區間[Li,Ri];
2 、選出一個引數 W;
3 、對於一個區間[Li,Ri],計算礦石在這個區間上的檢驗值Yi:
這批礦產的檢驗結果Y 為各個區間的檢驗值之和。即:Y1+Y2…+Ym
若這批礦產的檢驗結果與所給標準值S 相差太多,就需要再去檢驗另一批礦產。小T
不想費時間去檢驗另一批礦產,所以他想通過調整引數W 的值,讓檢驗結果儘可能的靠近
標準值S,即使得S-Y 的絕對值最小。請你幫忙求出這個最小值。
輸入輸出格式
輸入格式:
輸入檔案qc.in 。
第一行包含三個整數n,m,S,分別表示礦石的個數、區間的個數和標準值。
接下來的n 行,每行2個整數,中間用空格隔開,第i+1 行表示 i 號礦石的重量 wi 和價值vi。
接下來的m 行,表示區間,每行2 個整數,中間用空格隔開,第i+n+1 行表示區間[Li,Ri]的兩個端點Li 和Ri。注意:不同區間可能重合或相互重疊。
輸出格式:
輸出檔名為qc.out。
輸出只有一行,包含一個整數,表示所求的最小值。
輸入輸出樣例
輸入樣例#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 < wi, vi≤10^6,0 < S≤10^12,1 ≤Li ≤Ri ≤n 。
——————————————————————————————————————————-
注意
看資料範圍,有些變數要開long long
。
思路1
這是一個水思路:
在礦石重量Wi中找到一個最大值和一個最小值,作為二分的起點和終點,然後進行二分,因為這道題終答案具有遞增的性質,所以當算出來的值大於標準時,r = mid
,反之l = mid
。然後寫一個函式,用來計算每一次的答案,不斷更新
bool check(ll x) {
ll sum = 0;
ll he = 0;
ll ans = 0;
for(ll i = 1; i <= m; ++i) {
sum = 0, he = 0;
for(ll j = b[i].L; j <= b[i].R; ++j) {
if(a[j].w >= x) {
sum ++;
he += a[j].v;
}
}
ans += sum * he;
}
if(abs(ans - S) < ANS){
ANS = abs(ans - S);
}
if(ans > S) return true;
else return false;
}
-------------------------------
ll l = v[1], r = v[n];
ll mid;
while(l + 1 < r) {
mid = (l + r) / 2;
if(check(mid)){
l = mid;
}
else r = mid;
}
思路二
二分的基本思路不變,只是在計算的時候每次預處理一個字首和,這樣可以僅剩不少時間
for(int i=1;i<=n;++i){
if(W[i]>=w) v[i]=v[i-1]+V[i],num[i]=num[i-1]+1;
else v[i]=v[i-1],num[i]=num[i-1];
}
for(int i=1;i<=m;++i){
Y+=(v[R[i]]-v[L[i]-1])*(num[R[i]]-num[L[i]-1]);
}
最終程式碼
#include<bits/stdc++.h>
using namespace std;
const int N = 2e6 + 9;
long long n,m;
long long L[N],R[N];
long long W[N],V[N],num[N];
long long minx=1e7+1,maxx=-1;
long long ans=1e13+1,s;
long long Y=0;
long long v[N];
inline long long check(long long w){
Y=0;
memset(v,0,sizeof(v));
memset(num,0,sizeof(num));
for(int i=1;i<=n;++i){
if(W[i]>=w) v[i]=v[i-1]+V[i],num[i]=num[i-1]+1;
else v[i]=v[i-1],num[i]=num[i-1];
}
for(int i=1;i<=m;++i){
Y+=(v[R[i]]-v[L[i]-1])*(num[R[i]]-num[L[i]-1]);
}
return Y ;
}
int main (){
scanf("%lld%lld%lld",&n,&m,&s);
for(int i=1;i<=n;++i){
scanf("%lld %lld",&W[i],&V[i]);
minx=min(W[i],minx);
maxx=max(W[i],maxx);
}
for(int i=1;i<=m;++i){
scanf("%lld %lld",&L[i],&R[i]);
}
long long l=minx,r=maxx+1;
while(l+1<r){
long long mid=l+r>>1;
ans=min(ans,llabs(check(mid)-s));
if(Y>s) l=mid;
else r=mid;
}
long long ls=min(llabs(check(l)-s),llabs(check(r)-s));
printf("%lld\n",ls>ans?ans:ls);
}