P1314 聰明的質監員

2022-02-17 15:58:27 字數 3602 閱讀 8476

原題連線 

首先題號好評qwq~ 1314

意思就是:我們要在第 i 個區間 [ li , ri

] 裡找到所有的 j,使得 wj >= w,求出這些 j 的價值總和及符合條件的 j 的個數,那麼這個區間的貢獻就是這個價值總和乘上 j 的個數,然後我們要算所有區間的貢獻的總和 y ,最後輸出 y - s 的絕對值的最小值,其中 w 可以自己定 。

既然 w 可以自己定,那麼乙個很顯然的思路就是我們二分列舉 w,看看哪個 w 最合適不就好啦?

列舉的上下界

我們再回頭看那個公式,顯然若 w 變大,則 y 變小(符合條件的 j 的數量少了); 若 w 變小,則 y 變大;

那麼如果 w 一直變小,直到所有礦石的 wi 都大於我們列舉的這個 w 的話,那麼不就是所有的礦石都被我們給選上了?這時候就會產生最大的 y;

同理,當 w 比所有礦石的 wi 都大時,所有的礦石我們都沒選,這時候就會產生最小的 y ;

所以我們便得出來上下界:

下界:最小的 wi - 1 ;

上界:最大的 wi + 2,這裡為什麼是 + 2 呢?因為 + 1就是所有礦石都不選的情況,我們列舉的時候也要考慮到這種情況。所以再 + 1 就囊括了所有的情況了;

for(int i=1;i<=n;i++)

for(int i=1;i<=m;i++) //

m個區間

long

long left=minx_w-1; //

找上下界,這樣就能包含所有情況哦

long

long right=maxn_w+2;

二分過程當我們列舉的 w 求出的 y 過大時,我們應該增大 w 來使得 y 變小來盡量接近 s;當我們列舉的 w 求出的 y 過小時,我們應該減小 w 來使得 y 變大來盡量接近 s;然後我們在求過過程中對 | y - s | 一直取min 就行了。

求字首和

最後一部分就是求每個區間的貢獻,顯然我們要求出每個區間符合條件的 j 的個數和價值總和,如果我們對於每個區間我們都要 o(n)的列舉一遍的話, 總的時間複雜度應該是 o(nm log n),log n 是二分次數。對於 2e6 的資料,顯然炸的妥妥的,那怎麼辦呢?

我們可以用字首和鴨,對於我們列舉的每個 w ,我們都先預處理出 價值總和 ,符合條件的個數 這兩個字首和,然後每個區間我們 o(1)算出其貢獻值,這樣的時間複雜度應該是 o((n+m) log n),比較保險~ 

對於上面的時間複雜度的估算,本蒟蒻很可能算錯了,若發現錯誤請大佬們指出,感激不盡qwq~

那麼怎麼求字首和?

我們設: pre_num [ i ] 為第 1 個礦石到第 i 個礦石中符合條件的礦石數(條件:wj >= w),pre_v [ i ] 為第 1 個礦石到第 i 個礦石中所有符合條件的礦石的價值總和。

那麼我們可以得出遞推方程(狀態轉移方程):

for(int i=1;i<=n;i++)

else

//不滿足條件的話

}

這個還是比較顯然的吧qwq~

算區間貢獻

字首和做差大家應該都會,我們有了1~n的字首和了,那麼我們求區間 [ li 

, ri ] 內的符合條件的礦石數,就是區間 [ 1 , ri ] 內符合條件的礦石數減去區間 [ 1 , li -1] 內符合條件的礦石數,求區間的價值總和同理:

for(int i=1;i<=m;i++)                         //

m個區間

上完整的ac**:

#include#include

#include

using

namespace

std;

long

long

read()

while(ch>='

0'&&ch<='9'

)

return a*x;

}long

long

n,m,l,r;

long

long pre_num[2000001

];long

long nl[2000001],nr[2000001

];long

long

maxn_w,minx_w,minx,s;

long

long pre_v[2000001

];struct

node

a[2000001

];int work(long

long w) //

我們列舉的w

for(int i=1;i<=n;i++) //

預處理

else

//不滿足條件的話

}long

long y=0

;

for(int i=1;i<=m;i++) //

m個區間

long

long s=y-s;

if(s<0) s=-s; //

手寫取絕對值,用函式總有一堆奇怪的錯誤

if(s//

更新答案

if(y>s) return

1; //

y太大,需要增大w來減小y

if(yreturn

0; //

y太小,需要減小w來增大y

if(y==s) return -1; //

正好,一定是最優解

}int

main()

for(int i=1;i<=m;i++) //

m個區間

long

long left=minx_w-1; //

找上下界,這樣就能包含所有情況哦

long

long right=maxn_w+2

;

while(left<=right)

printf(

"%lld

",minx);

return0;

}

P1314 聰明的質監員

小t 是一名質量監督員,最近負責檢驗一批礦產的質量。這批礦產共有 n 個礦石,從 1到n 逐一編號,每個礦石都有自己的重量 wi 以及價值vi 檢驗礦產的流程是 1 給定m 個區間 li,ri 2 選出乙個引數 w 3 對於乙個區間 li,ri 計算礦石在這個區間上的檢驗值yi 這批礦產的檢驗結果y...

P1314 聰明的質監員

我是題面 讀完題後,我們會發現這道題的題意非常簡單,大意就是有n件物品,m個區間,求每個區間檢驗值之和,通過改變引數使標準值與檢驗值的差的絕對值最小 很明顯,檢驗值的變動只與引數有關,我們可以二分引數來搜尋答案 由題意可知,引數至小為0,至大為所有物品中最大的重量,再大則與至大值意義相同 那麼每次用...

P1314 聰明的質監員

p1314 聰明的質監員 這題還是挺有名的,然而是個假題 原因 但是它的思維方式還是挺重要的。思路 我們發現乙個w可以唯一確定乙個y,但是w不知道。像這種情況很容易想到二分。二分可以在 o log n 的時間內多個條件,有時候可以先假裝二分過了去想題,發現不用二分再把二分去掉。但是再算一算,只加上二...