[NOIP10.6模擬賽]1.merchant題解--思維+二分
阿新 • • 發佈:2018-10-07
函數 這樣的 分析 cpp 等於 read type reat gis
題目鏈接:
while(1)gugu(while(1))
閑扯
考場上怕T2正解寫掛其他兩題沒管只打了暴力,晚上發現這題思維挺妙的
同時想吐槽出題人似乎熱衷卡常...我的巨大常數現在顯露無疑QAQ
分析
這道題yy出了一個似乎比solution更好理解的解法,一開始有\(n\)條一次函數,就有\(2^n\)種函數集合,顯然每個集合也是一個一次函數\(T_i(x)=k_i x+b_i\)
我們把這個集合分成兩種\(k_i<=0\)和\(k_i>0\),顯然如果答案最後最大值的函數集合是第一種,那麽顯然肯定是在\(x=0\)取到的
所以我們單獨把\(x=0\)拎出來考慮就可以不考慮第一種函數集合的貢獻了
對於第二種\(k_i>0\)的函數集合,應該很容易發現\(max(T_i(x))\)是單調遞增的的圖像,可以二分找到答案要求的點
然後這題就做完了
對於0的處理其實就是把\(b_i\)最大且大於0的拿出來看看是否大於等於S就好了,雖然最後這樣的函數集合不一定是第一種\(k_i<=0\)但是一定考慮進去了
二分的時候也是貪心把該點的處於前\(m\)大且大於0的單個一次函數值加起來判斷一下就好了
這裏有個騷操作nth_ment(l,pos,r,cmp),表示將容器中\([l,r)\)種第pos個位置的元素變成第\(pos\)大/小(視cmp函數決定),同時pos前都是大/小於第pos大/小的元素,pos後類似
代碼
#include <cstdio> #include <cstdlib> #include <cctype> #include <algorithm> #include <cctype> #include <iostream> #include <queue> #include <vector> #define ll long long #define ri register int using std::min; using std::max; using std::nth_element; inline char nc(){ static char buf[100000],*p1=buf,*p2=buf; return p1==p2&&(p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++; } template <class T>inline void read(T &x){ x=0;int ne=0;char c; while(!isdigit(c=nc()))ne=c==‘-‘; x=c-48; while(isdigit(c=nc()))x=(x<<3)+(x<<1)+c-48; x=ne?-x:x;return ; } const int maxn=1000005; const int inf=0x7fffffff; ll v[maxn],ki[maxn],bi[maxn]; int n,m;ll s; bool ok(int x){ for(ri i=1;i<=n;i++)v[i]=ki[i]*x+bi[i]; nth_element(v+1,v+m,v+n+1,std::greater<ll>()); ll sum=0; if(sum>=s)return 1; for(ri i=1;i<=m;i++){ if(v[i]>0)sum+=v[i]; if(sum>=s)return 1; } return 0; } int main(){ freopen("merchant.in","r",stdin); freopen("merchant.out","w",stdout); read(n),read(m),read(s); for(ri i=1;i<=n;i++){ read(ki[i]),read(bi[i]); } if(ok(0)){puts("0");exit(0);} int L=1,R=1e9,ans; while(L<=R){ int mid=(L+R)>>1; if(ok(mid))ans=mid-1,R=mid-1; else L=mid+1; } printf("%d\n",ans+1); return 0; }
[NOIP10.6模擬賽]1.merchant題解--思維+二分