NWERC 2020A Atomic Energy(背包+思维)
对于这题,观察到数据范围,首先观察出第一个性质
最后只需要找到两个和大于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; }
没有人不辛苦,只有人不喊疼