【LG P1314】聰明的質監員
阿新 • • 發佈:2021-07-17
這個題很明顯的二分列舉,但是還有一個字首和有點坑人。
這題題其實點不多,就兩個關鍵點。
二分的判斷
可以看到:在W取0時,所有的區間內的礦石都可以選上,而在W大於最大的質量時,所有的礦石都選不上。
然後簡單算一下就發現:W越大,礦石選的越少,W越小,礦石選的越多。
所以,隨著W增大,Y值減小。
所以,二分的判斷條件出來了:
當 \(Y>s\) 時,需要增大 \(W\) 來減小 \(y\),從而 \(|W-s|\) 變小;
當 \(Y=s\) 時,\(|Y-s|=0\);
當 \(Y<s\) 時,需要減小 \(W\) 來增大 \(Y\) ,從而 \(|Y-s|\)變大。
字首和
我們在計算一個區間的和時(雖然這裡是兩個區間和再相乘,但沒關係),通常是用字首和的方法來縮減時間,直接模擬是 \(n^2\)
很顯然:
在 \(w_i>=W\) 時這個 \(i\) 礦石會在統計裡(若 \(<W\) 就不管它了直接 \(pre_i=pre_{i-1}\)),礦石價值和是:\(pre_v[i]=pre_v[i-1]+v[i]\),前面的和加上當前這一個 \(i\) 礦石;礦石數量和是:\(pre_n[i]=pre_n[i-1]+1\),數量加 \(1\) 嘛。然後最後算的時候用右端點 \(r-\)(左端點 \(l-1\))就可以了。
程式碼
#include<bits/stdc++.h> using namespace std; using LL=long long; const LL N=2e5+5; LL n,m,s,w[N],v[N],l[N],r[N],sum_n[N],sum_v[N],sum; bool check(int W) { LL y=0; sum=0,memset(sum_n,0,sizeof(sum_n)),memset(sum_v,0,sizeof(sum_v)); for(int i=1; i<=n; i++) { if(w[i]>=W) { sum_n[i]=sum_n[i-1]+1,sum_v[i]=sum_v[i-1]+v[i]; } else { sum_n[i]=sum_n[i-1],sum_v[i]=sum_v[i-1]; } } for(int i=1; i<=m; i++) { y+=(sum_n[r[i]]-sum_n[l[i]-1])*(sum_v[r[i]]-sum_v[l[i]-1]); } sum=llabs(y-s); return y>s; } int main() { scanf("%lld %lld %lld",&n,&m,&s); LL minn=LLONG_MAX>>1,maxn=0; for(int i=1; i<=n; i++) { scanf("%lld %lld",w+i,v+i),minn=min(minn,w[i]),maxn=max(maxn,w[i]); } for(int i=1; i<=m; i++) { scanf("%lld %lld",l+i,r+i); } LL left=minn-1,right=maxn+1,ans=LLONG_MAX>>1; while(left<=right) { LL mid=(left+right)>>1; if(check(mid)) { left=mid+1; } else { right=mid-1; } ans=min(ans,sum); // printf("%lld %lld %lld\n",right,mid,left);//Debug } printf("%lld",ans); return 0; }
參考資料
題解 P1314 【聰明的質監員】 - 人殤物已非 的部落格 - 洛谷部落格 (luogu.com.cn)
本作品採用 知識共享署名-非商業性使用-相同方式共享 4.0 國際許可協議 進行許可。
限於本人水平,如果文章有表述不當之處,還請不吝賜教。