【訓練記錄】2024年莆田市高中資訊學奧賽國慶集訓CSP-S提高組(第四天場外)

MNNUACM_2024ZY發表於2024-10-04

訓練情況

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;
}

相關文章