これ自明な特性だったのかな。
https://yukicoder.me/problems/no/2546
問題
N個の等差数列が与えられる。
各数列の先頭何個かを、計M個とり、その合計を最大化したい。その値を求めよ。
解法
公差が負とそうでないものを考える。
- 公差が負のものからいくつか取る場合、貪欲に「次に取れる値が最大のもの」を選んでいけばよい。
- 公差が非負のものからいくつか取る場合、1つの数列からすべて取ってしまうのが良い。
後者の場合、初項A、公差Dの数列の先頭K要素を取る場合、AK+D*K*(K-1)/2=K/2*(KD+A-2D)となる。
公差が負のものをX個、非負のものからN-X個取ることを考えXを総当たりしよう。
公差が負のX個の選び方は前述のとおり貪欲にとる。
残り非負のものからN-X個取るのは、複数の数列に対しK/2*(KD+A-2D)の最大値を求める問題なので、カッコ内の一次式に対しCHTを適用すればよい。
int N,M; ll A[303030],D[303030]; int cur[303030]; template<typename V> struct ConvexHull { deque<pair<V,V>> Q; V calc(pair<V,V> p, V x) { return p.first*x+p.second; } int dodo(pair<V,V> A,pair<V,V> B, pair<V,V> C) { return ((__int128)(B.second-C.second)*(B.first-A.first)<=(__int128)(A.second-B.second)*(C.first-B.first)); } void add(V a, V b) { // add ax+b if(Q.size() && Q.back().first==a) { //aが同じ場合 if(b<=Q.back().second) return; //maxの場合 Q.pop_back(); } Q.push_back({a,b}); int v; while((v=Q.size())>=3 && dodo(Q[v-3],Q[v-2],Q[v-1])) Q[v-2]=Q[v-1], Q.pop_back(); } void add(vector<pair<V,V>> v) { sort(v.begin(),v.end()); for(auto r=v.begin();r!=v.end();r++) add(r->first,r->second); } V query(V x) { int L=-1,R=Q.size()-1; while(R-L>1) { int M=(L+R)/2; (0^((calc(Q[M],x)<=calc(Q[M+1],x)))?L:R)=M; } return calc(Q[R],x); } }; ConvexHull<ll> ch; void solve() { int i,j,k,l,r,x,y; string s; cin>>N>>M; vector<pair<ll,ll>> C; C.push_back({-(3LL<<30),-(1LL<<40)}); priority_queue<pair<ll,int>> Q; FOR(i,N) { cin>>A[i]>>D[i]; if(D[i]>=0) { C.push_back({D[i],2*A[i]-D[i]}); } else { Q.push({A[i],i}); } } ch.add(C); A[N]=-1LL<<42; Q.push({A[N],N}); ll ret=M*ch.query(M)/2; ll sum=0; for(i=M-1;i>=0;i--) { auto p=Q.top(); Q.pop(); sum+=p.first; x=p.second; A[x]+=D[x]; Q.push({A[x],x}); ret=max(ret,sum+i*ch.query(i)/2); } cout<<ret<<endl; }
まとめ
2次式だからCHT使えないじゃん…と思ってしまった。