CF573E Bear and Bowling
阿新 • • 發佈:2021-10-21
一、題目
二、解法
首先有一個根本想不到比較顯然的貪心:直接貪心選取貢獻最大的 \(i\)
所以做題策略還是要調整啊,大膽猜結論,小心驗證,
如果能拍上幾萬組肯定就沒問題。
那麼每個 \(i\) 的貢獻可以寫成 \(k_ia_i+b_i\) 的形式,其中 \(k_i\) 表示前面已經選取的點數\(+1\),\(b_i\) 表示後面選取的點權值和,我們的任務就是動態維護這 \(n\) 個一次函式的最大值。
考慮修改的方式,選取一個點之後把一段字首的 \(b_i\) 加上某個值,把一段字尾的 \(k_i\) 加 \(1\)
可以考慮用凸包維護,這相當於有一條斜率為 \(-k\) 的直線來擷取若干個 \((a_i,b_i)\)
對於每個塊分別求凸包,如果是整體修改 \(b\) 那麼最優決策點不變,如果是整體修改 \(k\) 那麼最優決策點會往優移動,因為最優決策點是單調的,所以可以那一個指標去指一下最優決策點即可。
需要預先對塊內 \(a\) 排序,每次要重構被取點的凸包,時間複雜度 \(O(n\sqrt n)\)
三、解法
一次函式想凸包,奇怪修改用分塊。
#include <cstdio> #include <iostream> #include <algorithm> #include <cmath> using namespace std; const int M = 100005; const int N = 405; #define int long long #define pii pair<int,int> #define mp make_pair const int inf = 1e18; int read() { int x=0,f=1;char c; while((c=getchar())<'0' || c>'9') {if(c=='-') f=-1;} while(c>='0' && c<='9') {x=(x<<3)+(x<<1)+(c^48);c=getchar();} return x*f; } int n,m,ans,a[M],b[M],c[M],fk[N],fb[N],L[N],R[N]; int id[M],tp[N],nw[N],s[N][N]; int cmp(int x,int y) { return a[x]<a[y]; } double sp(int x,int y) { if(a[x]==a[y]) return b[x]>b[y]?-inf:inf; return (b[x]-b[y])/(a[x]-a[y]); } int cal(int x) { return fk[c[x]]*a[x]+b[x]+fb[c[x]]; } void build(int w)//build the convex of block w { //release the tag for(int i=L[w];i<=R[w];i++) b[i]+=fb[w]+fk[w]*a[i]; fb[w]=fk[w]=tp[w]=0;nw[w]=1; for(int i=L[w];i<=R[w];i++) { while(tp[w]>1 && sp(s[w][tp[w]-1],s[w][tp[w]]) <=sp(s[w][tp[w]],id[i])) tp[w]--; s[w][++tp[w]]=id[i]; } } pii ask(int w) { while(nw[w]<tp[w] && cal(s[w][nw[w]]) <=cal(s[w][nw[w]+1])) nw[w]++; return mp(cal(s[w][nw[w]]),s[w][nw[w]]); } signed main() { n=read();m=sqrt(n); for(int i=1;i<=n;i++) { a[i]=read();id[i]=i; c[i]=(i-1)/m+1; if(!L[c[i]]) L[c[i]]=i; R[c[i]]=i; } for(int w=1;w<=c[n];w++) { sort(id+L[w],id+R[w]+1,cmp); fk[w]=1;build(w); } for(int i=1;i<=n;i++) { pii mx=mp(0,0); for(int w=1;w<=c[n];w++) mx=max(mx,ask(w)); if(mx.first<=0) break; ans+=mx.first; int p=mx.second; for(int w=1;w<c[p];w++) fb[w]+=a[p]; for(int j=L[c[p]];j<p;j++) b[j]+=a[p]; for(int j=p+1;j<=R[c[p]];j++) b[j]+=a[j]; for(int w=c[p]+1;w<=c[n];w++) fk[w]++; b[p]=-inf;build(c[p]); } printf("%lld\n",ans); }