【训练记录】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;
}
posted @ 2024-10-04 12:57  MNNUACM_2024ZY  阅读(42)  评论(0编辑  收藏  举报