訓練情況
rk#1
\(100 + 100 + 100 + 100 = 400\)
賽後反思
因為滿分AK了,就不需要反思了
A題
顯然我們想要選的最多,我們優先選 \(a_i\) 小的,所以我們對 \(a_i\) 從小到大排序,再求一個字首和,再使用二分即可
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n,q; cin>>n>>q;
vector<int> a(n + 1);
vector<int> p(n + 1);
for(int i = 1;i<=n;i++) cin>>a[i];
sort(a.begin()+1,a.end());
for(int i = 1;i<=n;i++) p[i] = p[i-1] + a[i];
while(q--){
int x; cin>>x;
int pos = upper_bound(p.begin() + 1,p.end(),x) - p.begin() - 1;
cout<<pos<<endl;
}
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
B題
我們首先考慮什麼情況不可能,當操作2必須在操作1前面的時候為不可能,例如下面的這種情況
BBBBA
BBBBB
這種情況就是出現了操作2必須在操作1前面的情況,想要把 A 變成 B,在 A 的前面必須有一次操作一,但是一旦進行了操作 1 ,兩個字串就無法相等。
BAAAAA
AAAAAA
這種情況就是出現了操作2必須在操作1前面的情況,想要把 B 變成 A,操作 1 完成後,操作 2 必須在操作 1 的後面,但是後面任何一個操作 2 會使兩個字串就無法相等
如果存在有解的情況,我們當然要將操作 1 和操作 2 兩兩配對,但是同樣的,操作 1 必須在 操作 2 前面,如果出現了多個操作 2,沒有剩下操作 1 配對的話,也是對答案有貢獻的
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n; cin>>n;
string s; cin>>s;
string t; cin>>t;
for(int i = 0;i<n;i++){
if(t[i] == 'A') break; // BBBBA
if(s[i] == 'A'){ // BBBBB
cout<<-1<<endl;
return;
}
}
for(int i = n-1;~i;i--){ // BAAAAA
if(t[i] == 'B') break; // AAAAAA
if(s[i] == 'B'){
cout<<-1<<endl;
return;
}
}
int ans = 0;
int pair = 0;
for(int i = 0;i<n;i++){
if(s[i] == 'B' && t[i] == 'A') ans++,pair++;
if(s[i] == 'A' && t[i] == 'B'){
if(pair) pair--;
else ans++;
}
}
cout<<ans<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
C題
最後一個說有的人前面的人一定不會是小偷,如果前面的人是嫌疑人,那麼他進去的時候應該已經被偷了,他就說謊了。
第一個說沒有的人後面的人一定不會是小偷,如果後面的人是嫌疑人,那麼他進去的時候還沒被偷,他就說謊了。
但是隻有小偷會說謊,所以可以判斷兩種情況矛盾與否。
#include <bits/stdc++.h>
#define int long long
using namespace std;
void solve(){
int n; cin>>n;
int ans = n;
int l = 0,r = n + 1;
for(int i = 1;i<=n;i++){
int x; cin>>x;
if(x == 1) l = i;
if(x == 0) r = min(r,i);
}
if(r - l + 1 > n) cout<<n<<endl;
else cout<<max(0ll,r - l + 1)<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}
D題
我們可以反過來求答案,把減去的部分求出來,再使用 \(\sum_{i=1}^{n}a_i\) 去掉減去的部分即可,考慮區間DP,設計狀態 \(DP[0/1][l][r]\),表示區間 \([l,r]\) 選或不選。
對於區間求和的問題,我們先字首和預處理即可,DP的狀態轉移方程如下
\[dp[0][l][r]=min(dp[0][l+1][r]+(p[l]+p[n]-p[r]),dp[1][l+1][r]+((p[l]+p[n]-p[r])*len))
\]
\[dp[1][l][r]=min(dp[1][l][r-1]+(p[l-1]+p[n]-p[r-1]),dp[0][l][r-1]+((p[l-1]+p[n]-p[r-1])*len))
\]
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 2e3 + 3;
int dp[2][N][N];
void solve(){
int n; cin>>n;
vector<int> a(n + 1);
vector<int> b(n + 1);
vector<int> p(n + 1);
int s = 0;
for(int i = 1;i<=n;i++) cin>>a[i],s+=a[i];
for(int i = 1;i<=n;i++) cin>>b[i],p[i] = p[i-1] + b[i];
for(int i = 1;i<=n;i++) for(int j = 1;j<=n;j++) dp[0][i][j] = dp[1][i][j] = LONG_LONG_MAX;
for(int i = 1;i<=n;i++) dp[0][i][i] = dp[1][i][i] = 0;
for(int len=1;len<=n;len++)
for(int l=1;l+len<=n;l++){
int r=l+len;
dp[0][l][r]=min(dp[0][l+1][r]+(p[l]+p[n]-p[r]),dp[1][l+1][r]+((p[l]+p[n]-p[r])*len));
dp[1][l][r]=min(dp[1][l][r-1]+(p[l-1]+p[n]-p[r-1]),dp[0][l][r-1]+((p[l-1]+p[n]-p[r-1])*len));
}
cout<<s-min(dp[0][1][n],dp[1][1][n])<<endl;
}
signed main(){
// int T; cin>>T; while(T--)
solve();
return 0;
}