-
題意:一個含有\(k\)個中子的原子,如果\(k\le n\),那么它就直接爆炸釋放\(a_k\)大小的能量,否則它會分裂成含有\(i\)個和\(j\)個中子的原子(\(i+j=k\)),然后繼續分裂.給你\(n\)和\(a_1,a_2,...,a_n\),詢問\(q\)次,每次問你一個含有\(k\)個中子的原子分裂產生的最小能量.
-
題解:這題當時我開的時候,有一個很明顯的貪心思路,就是可以先處理\(a\),找出對答案貢獻最小的\(a_i\),然后對於\(k>n\)的原子,我們一定優先分裂\(i\)個最優,但是分裂到一定值的時候就不能繼續這樣分裂了,舉個例子,假設\(n=4,k=8\),\(a_1=10000,a_2=1,a_3=4,a_4=10000\),那么首先將\(8\)分裂\(2\)和\(6\),很顯然我們不能再將\(6\)分裂成\(2\)和\(4\),而應分成\(3\)和\(3\)最優.考慮到這,我想到可以類似完全背包來預處理前面一些狀況,然后對於很大的\(k\)可以分成兩段,一段用預處理的背包來給答案貢獻,另一段就是\(cnt*a_i\),但是這個預處理的常數給我卡死了,不知道為啥開小了會wa,如果有大佬知道請評論告訴孩子QAQ,另外,這題可以不找對答案貢獻最小的\(a_i\),\(n\)很小,完全可以枚舉.
-
代碼:
#include <bits/stdc++.h> #define ll long long #define fi first #define se second #define pb push_back #define me memset #define rep(a,b,c) for(int a=b;a<=c;++a) #define per(a,b,c) for(int a=b;a>=c;--a) const int N = 5e6 + 10; const int mod = 1e9 + 7; const int INF = 0x3f3f3f3f; using namespace std; typedef pair<int,int> PII; typedef pair<ll,ll> PLL; ll gcd(ll a,ll b) {return b?gcd(b,a%b):a;} ll lcm(ll a,ll b) {return a/gcd(a,b)*b;} int n,q; ll a[N]; ll dp[N]; int main() { ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); cin>>n>>q; rep(i,1,n) cin>>a[i]; rep(i,1,5000000) dp[i]=5e18; rep(i,1,n) dp[i]=a[i]; rep(j,n+1,5000000){ rep(i,1,n){ dp[j]=min(dp[j],dp[j-i]+dp[i]); } } while(q--){ ll ans=5e18; ll k; cin>>k; if(k<=1000000){ cout<<dp[k]<<'\n'; continue; } k-=1000000; rep(i,1,n){ ll cnt=k/i; ll rest=k%i; ans=min(ans,cnt*a[i]+dp[1000000+rest]); } cout<<ans<<'\n'; } return 0; }