【训练记录】2024年莆田市高中信息学奥赛国庆集训CSP-S提高组(第三天场外)

训练情况

rk#37

\(40 + 0 + 0 + 0 = 40\)

看到满场原题就不是很想打了TAT

赛后反思

A题错误预估了算法时间复杂度,导致了一些不必要的失分

A题



首先显然我们发现因数 \(2,5\) 能变出一个 \(10\) 来,只需要统计 \({a_i}\) 里含有因数 \(2,5\) 的个数,最后答案取小值即可。

#include<bits/stdc++.h>
#define int long long
#define double long double
using namespace std;
int n,x,cnt2,cnt5;
signed main(){
	ios::sync_with_stdio(false);
	cin.tie(0),cout.tie(0);
    freopen("zero.in","r",stdin);
    freopen("zero.out","w",stdout);
	cin>>n;
	while(n--){
		cin>>x;
        if(x==0){
            cout<<1;
            return 0;
        }
		while(x%2==0){
			x/=2;
			cnt2++;
		}
		while(x%5==0){
			x/=5;
			cnt5++;
		}
	}
	cout<<min(cnt2,cnt5);
	return 0;	
}

B题





触发关键词《最小值最大》,观察到二分单调性,显然采用二分的方法。

对于每一门武学:

如果 \(a_i < b_i\),那么这门武学全部通过自学,所需次数为 \(\lceil \frac{k}{b_i} \rceil\)

如果 \(a_i > b_i\),那么这门武学优先接受指点,如果只接受指点能达成目标 \(ma_i \ge k\),所需次数为 \(\lceil \frac{k}{b_i} \rceil\)

否则,先接受指点,再自学,所需次数为 \(\lceil \frac{k - ma_i}{b_i} \rceil + m\)

#include<iostream>

using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
ll n, m, a[N], b[N];
// 要让每一门课都达到x的熟练度
bool check(ll x) {
    // n m
    ll cnt = 0;
    for (int i = 1; i <= n; i++) {
        if (a[i] <= b[i]) { // 必然翘课上
            cnt += (x + b[i] - 1) / b[i];
        } else if (m * a[i] >= x) { // 正常上课够x,那就只正常上课
            cnt += (x + a[i] - 1) / a[i];
        } else { // 不够的话,就先上m次a,再翘课
            cnt += m + (x - a[i] * m + b[i] - 1) / b[i];
        }
        if (cnt > n * m) {
            return false;
        }
    }
    return cnt <= m * n;
}
int main() {
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> a[i];
    for (int i = 1; i <= n; i++) cin >> b[i];
    ll l = 1, r = 1e18 + 5, ans;
    while (l <= r) {
        ll mid = (l + r) >> 1;
        if (check(mid)) {
            ans = mid;
            l = mid + 1;
        } else {
            r = mid - 1;
        }
    }
    cout << ans << endl;
    return 0;
}

C题




上司参加或者不参加,员工都可以参加舞会。当上司不参加,员工参加时,他的这个员工会产生双倍的快乐值。
设计 \(DP[i][0/1]\) 分别表示 \(i\) 号参不参加舞会,\(A[i]\) 表示 \(i\) 来参加舞会时的快乐值,那么有

\[DP[i][0] = \sum_{j \in son_i} max(DP[j][0],DP[j][1] + A[j]) \]

\[DP[i][1] = A[i] + \sum_{j \in son_i} max(DP[j][0],DP[j][1]) \]

#include<bits/stdc++.h>
using namespace std;
int n,r[6005],f[6005][2],ans=0;
vector<int>v[6005];
bool book[6005];
void dfs(int root){
	for(auto i:v[root]){
		dfs(i);
		f[root][0]+=max(f[i][1]+r[i],f[i][0]);
		f[root][1]+=max(f[i][1],f[i][0]);
	}
	f[root][1]+=r[root],ans=max(ans,max(f[root][1],f[root][0]));
	return;
}
int main(){
	freopen("dance.in","r",stdin);
	freopen("dance.out","w",stdout);
	ios::sync_with_stdio(0);
	cin.tie(0),cout.tie(0);
	cin>>n;
	for(int i=1;i<=n;++i)cin>>r[i];
	for(int i=1;i<n;++i){
		int uu,vv;
		cin>>uu>>vv,v[vv].emplace_back(uu),book[uu]=true;
	}
	for(int i=1;i<=n;++i)if(!book[i]){
		dfs(i),cout<<ans;
		return 0;
	}
	return 0;
}

D题







对字典内的单词建立字典树,维护每个结点对应的总频率信息,比如某结点经过了两次,两次的单词的频率都要加上来。

查询时,可以对每一层都以深度和按键对应的字母路径做限制,都进行一次dfs,找到当前路径下出现频率最大的那个组合。

#include<iostream>

using namespace std;
const int N = 1e5 + 5;
int n, m, idx, ans;
int L[] = {
    0,
    0,
    'a',
    'd',
    'g',
    'j',
    'm',
    'p',
    't',
    'w'
};
int R[] = {
    0,
    0,
    'd',
    'g',
    'j',
    'm',
    'p',
    't',
    'w',
    'z' + 1
};
char C[N], A[N];
struct node {
    int w, to[26];
}
trie[N];
void add(string p, int w) {
    int len = p.length(), id = 0;
    for (int i = 0; i < len; i++) {
        int c = p[i] - 'a';
        if (trie[id].to[c] == 0) trie[id].to[c] = ++idx;
        id = trie[id].to[c];
        trie[id].w += w;
    }
}
string p;
int dfs(int len, int dep, int id, int sum) {
    if (dep == len) {
        if (sum > ans) {
            for (int i = 1; i <= len; i++) A[i] = C[i];
            ans = sum;
        }
        return 1;
    }
    int flag = 0;
    for (int i = L[p[dep] - '0']; i < R[p[dep] - '0']; i++) {
        int c = i - 'a';
        if (trie[id].to[c] == 0) continue;
        C[dep + 1] = i;
        if (dfs(len, dep + 1, trie[id].to[c], trie[trie[id].to[c]].w)) flag = 1;
    }
    return flag;
}
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >> n;
    for (int i = 1; i <= n; i++) {
        int w;
        cin >> p >> w;
        add(p, w);
    }
    cin >> m;
    while (m--) {
        cin >> p;
        int e = p.length();
        for (int i = 1; i < e; i++) {
            ans = 0;
            if (dfs(i, 0, 0, 0))
                for (int j = 1; j <= i; j++) cout << A[j];
            else cout << "MANUALLY";
            cout << "\n";
        }
        cout << "\n";
    }
    return 0;
}
posted @ 2024-10-03 11:40  MNNUACM_2024ZY  阅读(37)  评论(1编辑  收藏  举报