2024年7月上海月賽乙組

forleaves發表於2024-08-16

冪的運算

題目:給定a, b, c, 求 \(a^{b}\) mod c。

分析:快速冪板子題

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
long long n, m, k;
long long ksm(long long a, long long b, long long p) {
	long long ans = 1;
	while (b) {
		if (b & 1) ans = ans * a % p;
		a = a * a % p;
		b >>= 1;
	}
	return ans;
}
int main(){
	cin >> n >> m >> k;
	cout << ksm(n, m, k) << endl;
	return 0;
}

選舉快報

題目:每次給定一個提名的候選者的名字,輸出當前候選者出現次數最多的名字,相同輸出字典序排名靠前的候選者。

分析:優先佇列實時維護,map對映字串對應數量

程式碼:

#include <bits/stdc++.h>
using namespace std;
const int N = 2e5 + 10;
int n, m, k;
string str;
struct node {
	int x;
	string name;
	bool operator < (const node& a) const {
		if (x == a.x)
			return name > a.name;
		return x < a.x;
	}
};
priority_queue<node> q;
map<string, int> ma;
int main(){
	cin >> n;
	for (int i = 1; i <= n; i++) {
		cin >> str;
		ma[str]++;
		q.push({ma[str], str});
		cout << q.top().name << endl;
	}
	return 0;
}

倒水問題

題目:三個沒有刻度的杯子,容量分別為 a 升、b 升與 c 升。一開始,只有容量為 c 的杯子中灌滿了水,另外兩個杯子是空的。可以將水從一個杯子倒去另一個杯子,倒水過程直到原杯子變空或新杯子變滿才會停止。水只能在杯子間轉移,不會憑空增加或減少。請輸出最少能有多少水可以出現在某個杯子裡,並輸出最少需要多少步才能達到這一目的。

分析:a,b,c範圍5000內,看資料才時間複雜度\(O(n^{2})\),三個杯子,兩個杯子裝的水確定了,第三個杯子裝的水就也確定了,狀態暴力轉移,有水就可以轉移,轉移分為可能裝滿,和裝不滿,分類清楚即可。

程式碼:

#include<bits/stdc++.h>
using namespace std;
int n, m, k;
int ans = 5001, cnt;
struct node {
	int a, b, c, step;
}; 
bool f[5010][5010];
queue<node> q;
int main(){
	cin >> n >> m >> k;
	q.push({0, 0, k, 0});
	while (!q.empty()) {
		node x = q.front();
		q.pop();
		int a = x.a, b = x.b, c = x.c, step = x.step;
		if (a && a < ans) {
			ans = a;
			cnt = step;
		}
		if (b && b < ans) {
			ans = b;
			cnt = step;
		}
		if (c && c < ans) {
			ans = c;
			cnt = step;
		}
		if (a) {
			if (a + b >= m && !f[a+b-m][m]) {
				f[a+b-m][m] = true;
				q.push({a+b-m, m, c, step+1});
			}
			else if (a + b < m && !f[0][a + b]) {
				f[0][a+b] = true;
				q.push({0, a+b, c, step+1});
			}
			if (!f[0][b]) {
				f[0][b] = true;
				q.push({0, b, c + a, step + 1});
			}
		}
		if (b) {
			if (a + b >= n && !f[n][a + b - n]) {
				f[n][a + b - n] = true;
				q.push({n, a + b - n, c, step+1});
			}
			else if (a + b < n && !f[a + b][0]) {
				f[a+b][0] = true;
				q.push({a+b, 0, c, step+1});
			}
			if (!f[a][0]) {
				f[a][0] = true;
				q.push({a, 0, c + b, step + 1});
			}
		}
		if (c) {
			if (c + a >= n && !f[n][b]) {
				f[n][b] = true;
				q.push({n, b, c+a-n, step+1});
			}
			else if (c + a < n && !f[c+a][b]) {
				f[c+a][b] = true;
				q.push({c+a, b, 0, step+1});
			}
			if (c + b >= m && !f[a][m]) {
				f[a][m] = true;
				q.push({a, m, c+b-m, step+1});
			}
			else if (c + b < m && !f[a][c+b]) {
				f[a][c+b] = true;
				q.push({a, c+b, 0, step+1});
			}
		}
	}
	cout << ans << endl << cnt << endl;
	return 0;
}

修改迴文(二)

題目:給定一個僅由小寫字母組成的字串 s ,你可以新增一些字元(也可以不加),使其構成迴文串。請你輸出在新增字元數最少的前提下,能夠構成字典序最小的迴文串。

分析:\(1e^{3}\)範圍想\(n^{2}\),新增字元數最少的迴文,就可以用區間dp的思路,只看最外圍的情況,比如從l 到 r的區間 如果第l個字元和第r個字元一樣,那數量就和從l+1到r-1的數量一樣,如果不是就是看從l到r-1和從l+1到r範圍內哪個少就是哪個,如果都一樣那哪個對於數量來說都一樣,但要求構成字典序最小,然後需要比第l個字元和第r個字元的ascll碼,哪個小加哪個,所以透過分析知道我們要預處理長度為1和長度為2的字串,然後dp刷,如果直接維護答案,那麼時間複雜度是 \(n^{3}\) 就tle了,所以我們先維護字串數最少是多少,然後在這個條件下去回溯找到這個最小的迴文串是什麼。

程式碼:

#include<bits/stdc++.h>
using namespace std;
string str;
int dp[1010][1010]; //dp[i][j]表示從str[i]到str[j]透過新增字元數構成迴文串的字串最少數量
string sol(int l, int r) {
	if (l == r) return str.substr(l, 1);
	if (l + 1 == r) {
		if (dp[l][r] == 0) return str.substr(l, 2);
		if (str[l] < str[r]) return str.substr(l, 2) + str[l];
		return str[r] + str.substr(l, 2);
	}
	if (str[l] == str[r]) {
		return str[l] + sol(l + 1, r - 1) + str[r];
	}
	if (dp[l + 1][r] < dp[l][r - 1]) {
		return str[l] + sol(l + 1, r) + str[l];
	}
	if (dp[l + 1][r] > dp[l][r - 1]) {
		return str[r] + sol(l, r - 1) + str[r];
	}
	if (str[l] < str[r]) {
		return str[l] + sol(l + 1, r) + str[l];
	}
	return str[r] + sol(l, r - 1) + str[r];
}
int main(){
	cin >> str;
	int n = str.size();
	str = '0' + str;
	for (int i = 1; i <= n; i++) {
		dp[i][i] = 0;
	}
	for (int l = 1; l + 1 <= n; l++) {
		if (str[l] != str[l + 1]) 
			dp[l][l + 1] = 1;
	}
	for (int i = 3; i <= n; i++) {
		for (int l = 1; l + i - 1 <= n; l++) {
			int r = l + i - 1;
			if (str[l] == str[r])
				dp[l][r] = dp[l+1][r-1];
			else {
				dp[l][r] = min(dp[l+1][r], dp[l][r-1]) + 1;
			}
		}
	}
	cout << sol(1, n) << endl;
	return 0;
}

相關文章