對於這題,觀察到數據范圍,首先觀察出第一個性質
最后只需要找到兩個和大於n的,減去這兩個值后,k就可以通過任意選擇組合而來。
如果k的數據范圍很小,那么我們可以直接背包求取答案。
現在k有1e9,那就需要其他的性質。
又因為我們觀察到數是連續的,因此假如當時我們有一個效率最高的數,也就是a[i]/i最小的那個。
那么最后的正解里面,一定有大部分數和可以成為這個i的倍數,這樣就可以用這個i來替換掉,從而縮減范圍。
由於數是連續的,根據鴿巢原理,最后剩下不能組成i的倍數的數不超過m+1個。我們預處理前面的背包。
所以我們只需要找到一個比這個上界大的數,那么k就是由j*am+f[k-j*am]更新而來。
這一步是因為由於在正解中和不為選中i的數最多m+1個,因此用他們組成的結果不會大於(m+1)*n,所以大於他的數都需要am來更新。

#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef pair<int,int> pll; const int N=1e6+10; const int mod=998244353; ll f[N]; ll a[N]; ll ff[N]; int m; int main(){ ios::sync_with_stdio(false); int i,j; int n,q; cin>>n>>q; for(i=1;i<=n;i++){ cin>>a[i]; if(!m||1.0*a[i]/i<1.0*a[m]/m){ m=i; } } for(i=0;i<N;i++){ f[i]=1e12; ff[i]=1e12; } f[0]=0; for(i=1;i<=n;i++){ for(j=i;j<=2e4;j++){ f[j]=min(f[j],f[j-i]+a[i]); } } for(i=1;i<=n;i++){ for(j=n-i+1;j<=n;j++){ ff[i+j]=min(ff[i+j],a[i]+a[j]); } } while(q--){ ll k; cin>>k; ll ans=1e18; if(k<=n){ cout<<a[k]<<endl; continue; } for(i=n+1;i<=2*n;i++){ if(i>k) continue; ll g=k-i; ll tmp=ff[i]; if(g<=2e4){ ans=min(ans,tmp+f[g]); continue; } ll d=g-2e4; d=d/m+1; int sign=0; tmp+=(d*a[m])+f[g-d*m]; ans=min(ans,tmp); } cout<<ans<<endl; } return 0; }