【训练记录】2024年莆田市高中信息学奥赛国庆集训CSP-S提高组(第四天场外)
训练情况
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;
}