洛谷題解:P1314 [NOIP2011 提高組] 聰明的質監員
洛谷題解:P1314 [NOIP2011 提高組] 聰明的質監員
題目:
小T 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n個礦石,從 1 到 n 逐一編號,每個礦石都有自己的重量 wi以及價值 vi 。檢驗礦產的流程是:
1 、給定m個區間 [li,ri]
2 、選出一個引數 W;
3 、對於一個區間 [li,ri],計算礦石在這個區間上的檢驗值 yi :
其中 j 為礦石編號。
這批礦產的檢驗結果 y 為各個區間的檢驗值之和。即:
若這批礦產的檢驗結果與所給標準值 s 相差太多,就需要再去檢驗另一批礦產。小T 不想費時間去檢驗另一批礦產,所以他想通過調整引數 W 的值,讓檢驗結果儘可能的靠近標準值 s,即使得 ∣s−y∣ 最小。請你幫忙求出這個最小值。
輸入格式:
第一行包含三個整數 n,m,分別表示礦石的個數、區間的個數和標準值。
接下來的 n 行,每行兩個整數,中間用空格隔開,第 i+1 行表示 i 號礦石的重量 wi 和價值 vi。
接下來的 m 行,表示區間,每行兩個整數,中間用空格隔開,第 i+n+1行表示區間 [li,ri] 的兩個端點 li 和 ri。注意:不同區間可能重合或相互重疊。
輸出格式:
一個整數,表示所求的最小值。
樣例:
輸入:
5 3 15
1 5
2 5
3 5
4 5
5 5
1 5
2 4
3 3
輸出:
10
分析:
可以看出來,這個函式是隨W的增加遞減的,由於要對W進行查詢遍歷,所以使用二分,找到一個結果最接近s的W。兩個端點的值自然是w陣列的最小值與最大值。
然後check()
的就是公式結果的值與s的大小關係,如果ans > s
,說明ans還需要再小,函式還是單減的,所以要選擇W更大的區間。
然後對於這個函式值的計算,對於每一個W,都要迴圈m次,每一次迴圈內還要從l[i]
到r[i]
再迴圈進行判斷,這樣執行了TLE了。看來是需要用字首和,這樣就免去了從l[i]
到r[i]
的迴圈的複雜度,但是需要提前對陣列單另判斷一次,不符合要求的不自增。
程式碼如下:
#include<iostream>
#include<string>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<vector>
using namespace std;
long long n, m, s;
long long w[300005] = {0};
long long v[300005] = {0};
long long l[300005] = {0};
long long r[300005] = {0};
long long pre_w[300005] = {0};
long long pre_v[300005] = {0};
long long mini = 2147483647;
long long maxi = -1;
long long result = 0;
long long C_Y(long long W)
{
long long ans = 0;
memset(pre_v,0,sizeof(pre_v));
memset(pre_w,0,sizeof(pre_w));
for(long long i = 1;i <= n;i++)
{
if(w[i] >= W)
{
pre_w[i] = pre_w[i - 1] + 1;
pre_v[i] = pre_v[i - 1] + v[i];
}
else
{
pre_w[i] = pre_w[i - 1];
pre_v[i] = pre_v[i - 1];
}
}
for(long long i = 1;i <= m;i++)
{
ans += (pre_w[r[i]] - pre_w[l[i] - 1]) * (pre_v[r[i]] - pre_v[l[i] - 1]);
}
return ans;
}
bool check(long long ans)
{
result = llabs(ans - s);
if(ans > s)
{
return true;
}
else
{
return false;
}
}
int main()
{
cin>>n>>m>>s;
for(long long i = 1;i <= n;i++)
{
cin>>w[i]>>v[i];
maxi = max(maxi, w[i]);
mini = min(mini, w[i]);
}
for(long long j = 1;j <= m;j++)
{
cin>>l[j]>>r[j];
}
long long left = mini, right = maxi;
long long mid;
long long ans = 0x3f3f3f3f3f3f3f3f;
while(left <= right)
{
mid = (right + left) / 2;
if(check(C_Y(mid)))
{
left = mid + 1;
}
else
{
right = mid - 1;
}
if(ans > result)
{
ans = result;
}
}
cout<<ans<<endl;
return 0;
}