ARC128 A-D簡要題解
ARC128 A-D簡要題解
A
題意
初始給定\(1\)個物品1,\(0\)個物品2 給定序列\(A_i\),每次可以把所有物品1變為\(A_i\)倍物品2,或者把所有物品2變為\(\frac{1}{A_i}\) 倍物品1
每次可以選擇操作或者不操作,求最終的操作序列
\[2 \leq N \leq 2\times10^5\\ 1 \leq A_i \leq 10^9 \]分析
顯然我們有一個無腦的DP做法:
\(dp[i][0/1]\)表示前\(i\)次選擇中進行了偶數/奇數次操作
轉移\(dp[i][0] = max(dp[i -1][0],dp[i-1][1] / A[i]),dp[i][1] = max(dp[i-1][0] \times A[i],dp[i-1][1])\)
DP過程中記錄轉移路徑,最後遞迴輸出即可,這樣做的問題是\(A_i\)值域過大,\(dp\)陣列難以儲存 處理方法可以用對所有數取log,當然這在有些情況可能出現精度問題
事實上注意到\(\frac{a}{b} \times \frac{b}{c} = \frac{a}{c}\) 所以直接貪心即可
程式碼
const int maxn = 2e5 + 5; double dp[maxn][2]; double a[maxn]; int path[maxn][2]; int n; void dfs(int n,int op){ if(!n) return; dfs(n - 1,path[n][op] ? op ^ 1 : op); printf("%d ",path[n][op]); } int main(){ n = rd(); for(int i = 1;i <= n;i++) a[i] = rd(); dp[0][0] = log(1); dp[0][1] = -1e9; for(int i = 1;i <= n;i++){ double x = dp[i - 1][0]; double y = dp[i - 1][1] - log(a[i]); if(x > y) path[i][0] = 0; else path[i][0] = 1; dp[i][0] = max(x,y); double xx = dp[i - 1][0] + log(a[i]); double yy = dp[i - 1][1]; if(xx > yy) path[i][1] = 1; else path[i][1] = 0; dp[i][1] = max(xx,yy); } dfs(n,0); }
B.
題意
給定\(R\)個紅球,\(G\)個綠球,\(B\)個藍球
可以做任意次以下操作:選擇兩個不同顏色的球把他們變為剩下顏色的球
求最小的操作次數使得所有球變為同一個顏色
\[1 \leq R,G,B \leq 10^8 \]分析
操作其實就是讓兩個數-1,另一個數+2
手玩一下可以發現,通過3步操作,可以變為0,-3,+3
這表示3步操作可以只改變其中兩個數,改變的值是3,如果能夠讓三個數中某兩個數相等,就可以不斷對這兩個操作達到結果
因為只需要列舉不變的數,判剩下的數是否同餘3即可
程式碼
int main(){ int T = rd(); while(T--){ int a = rd(); int b = rd(); int c = rd(); int ans = 1e9; if(abs(a - b) % 3 == 0) { int res = abs(a - b) / 3 * 3; res += min(a,b); ans = min(ans,res); } if(abs(a - c) % 3 == 0) { int res = abs(a - c) / 3 * 3; res += min(a,c); ans = min(ans,res); } if(abs(b - c) % 3 == 0) { int res = abs(b - c) / 3 * 3; res += min(b,c); ans = min(ans,res); } if(ans == 1e9) puts("-1"); else cout << ans << '\n'; } }
C
題意
給定數\(M,S\)
給定長度\(N\)的陣列\(A\)
構造長度\(N\)的陣列\(X\)滿足
\[0 \leq x_1 \leq x_2...\leq x_n \leq M\\ \sum x_i = S \]\[1 \leq N \leq 5000\\ 1 \leq M \leq 10^6\\ 1 \leq S \leq min(N \times M,10^6)\\ 1 \leq A_i\leq 10^6 \]分析
貪心性質還是比較明顯吧
\(x\)分配總是對一段字尾滿足\(x_i = x_{i + 1} = ...x{N}\)
若存在\(j <i\)且\(x_j != 0\) ,把這個\(x_j\)分配到\(x_i\)中最大的顯然更優
那麼考慮字尾有數的情況就有兩種:
大小超過\(M\) ,這個時候說明有多餘的\(S\),此時只需對前一段繼續做一次貪心
沒有超過\(M\),全填\(M\)
第一種情況的不超過\(M\)是可以欽定的,其實寫起來還是有點煩瑣
程式碼
const int maxn = 2e5 + 5;
int main(){
int n = rd();
int m = rd();
int s = rd();
VI a(n);
for(int i = 0;i < n;i++)
a[i] = rd();
VI x(n + 1),y(n + 1);
ll sum = 0;
for(int i = n - 1;i >= 0;i--){
sum += a[i];
x[n - i] = (ll)(n - i) * m;
y[n - i] = (ll)sum * m;
}
double ans = 0;
for(int i = 0;i <= n;i++){
if(x[i] <= s)
ans = max(ans,(double)y[i]);
}
for(int i = 0;i <= n;i++){
if(x[i] < s) {
for(int j = 0;j <= n;j++){
if(x[j] > s) {
ans = max(ans,((double)y[i] * (x[j] - s) + (double)y[j] * (s - x[i])) / (double)(x[j] - x[i]));
}
}
}
}
printf("%.10f",ans);
}
D
題意
給定\(N\)個數,若存在連續的\(x,y,z\)滿足\(A_x \neq A_y ,A_y \neq A_z\) 就可以刪除\(A_y\)
可以執行無限次操作,求剩餘序列的下標組成的序列個數
\[2 \leq N \leq 200000\\ 1 \leq A_i \leq N \]分析
顯然如果存在\(A_i = A_{i + 1}\)那麼\(i,i+1\) 必定無法刪除,換言之兩邊的方案獨立
於是問題變為了不存在\(A_i = A_{i+1}\)的情況,可以歸納得出若連續的一段中存在大於等於3種數,則可以任意刪除,於是可以DP
考慮轉移需要注意\(A_{i-1}= A_{i+1}\)的地方
程式碼
int a[maxn];
int solve(int l,int r){
int len = r - l + 1;
if(len <= 2) return 1;
VI v(len + 1),pre(len + 1),sum(len + 1),dp(len + 1);
for(int i = 1;i <= len;i++)
v[i] = a[l + i - 1];
pre[1] = 1;
pre[2] = 1;
for(int i = 3;i <= len;i++){
pre[i] = i - 1;
if(v[i] == v[i - 2]) pre[i] = pre[i - 1];
}
dp[1] = dp[2] = sum[1] = 1;
sum[2] = 2;
for(int i = 3;i <= len;i++){
dp[i] = (dp[i - 1] + dp[i - 2]) % MOD;
add(dp[i],sum[min(i - 2,pre[i]) - 1]);
sum[i] = (sum[i - 1] + dp[i]) % MOD;
}
return dp[len];
}
int main(){
int n = rd();
for(int i = 1;i <= n;i++)
a[i] = rd();
int l = 1;
int ans = 1;
for(int i = 1;i < n;i++){
if(a[i] == a[i + 1]) {
ans = mul(ans,solve(l,i));
l = i + 1;
}
}
ans = mul(ans,solve(l,n));
cout << ans;
}