2025 年中国大学生程序设计竞赛全国邀请赛(东北)

vp完东北的感觉就是,还是很讨厌写模拟啊,我不知道为什么感觉其实可以写别的题,但是我还是只写了六题,然后有一题是ai跑了一下(A)主要是优化不会吧,写的实在是太...暴力了

A:这题就是我用ai跑了一下我自己的超级暴力代码,然后 跑出来了,最后看了下题解是用st表,然后二分找出答案,但是我感觉可以用dp写,希望有老哥可以分享一下做法在评论区

题目大意: 给一段数组,有l,r两个指针,当1<=l,r<=n时有多少区间lr中gcd和min值相等,有多少种可能性

这个ai是帮我用hash然后其实也有点st表的味道写的

struct hash_pair {
    template <class T1, class T2>
    size_t operator()(const pair<T1, T2>& p) const {
        auto hash1 = hash<T1>{}(p.first);
        auto hash2 = hash<T2>{}(p.second);
        return hash1 ^ hash2;
    }
};
 
ll gcd(ll a, ll b) { return b ? gcd(b, a % b) : a; }
 
void solve() {
    ll n;
    cin >> n;
    vector<ll> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i];
    }
    ll c = 0;
    vector<tuple<ll, ll, ll>> prev;
    for (int i = 0; i < n; ++i) {
        unordered_map<pair<ll, ll>, ll, hash_pair> curr_map;
        // 单独处理当前元素形成的子数组
        ll current_g = a[i];
        ll current_m = a[i];
        pair<ll, ll> key(current_g, current_m);
        curr_map[key] = 1;
 
        // 合并前一个元素的所有可能子数组
        for (auto &t : prev) {
            ll g_prev = get<0>(t);
            ll m_prev = get<1>(t);
            ll cnt_prev = get<2>(t);
            ll new_g = gcd(g_prev, a[i]);
            ll new_m = min(m_prev, a[i]);
            pair<ll, ll> new_key(new_g, new_m);
            curr_map[new_key] += cnt_prev;
        }
 
        // 转换并统计符合条件的子数组数目
        vector<tuple<ll, ll, ll>> curr;
        for (auto &entry : curr_map) {
            ll g = entry.first.first;
            ll m = entry.first.second;
            ll cnt = entry.second;
            curr.emplace_back(g, m, cnt);
            if (g == m) {
                c += cnt;
            }
        }
        prev = move(curr); // 更新prev为当前状态
    }
    cout << c << endl;
}

F:中间的bcde好像都没开,但是其实好像可以开b我感觉,但是昨天晚上写的头晕眼花就下播了

 这题是公认的shi题,最无脑的大模拟,老老实实算角度,踏踏实实一点一点算不要找技巧是模拟题最大的技巧,看出来是模拟就老老实实的吧shi吃完是最对的选择,但是每次都希望在shi里面找到出题人的捷径,说白了还是好高骛远了

大体题意:给定3个时间点,从第一个时间点到第二和第三时间点的区间内最少需要转的角度是多少,输出转完后的时间

void solve() {
    ll x0, y0, x1, y1, x2, y2;
    cin >> x0 >> y0 >> x1 >> y1 >> x2 >> y2;
    pair<ll, ll>t1 = { x1,y1 }, t2 = { x2,y2 }, t3;
    ld ans = N;
    ld e = (x0 * 30 + y0 * 0.5), e1 = (y0 * 6.0);
    while (t2 >= t1) {
        ll x = t1.first, y = t1.second;
        ld r1 = ((x * 30.0) + y * 0.5);
        ld r2 = y * 6.0;
        ld q1 = min(abs(e - r1), abs(360 - abs(e - r1)));
        ld q2 = min(abs(e1 - r2), abs(360 - abs(e1 - r2)));
        if (q1 + q2 < ans) {
            ans = q1 + q2;
            t3 = { x,y };
        }
        t1.second++;
        if (t1.second == 60) {
            t1.first++;
            t1.second = 0;
        }
    }
    cout << t3.first << " " << t3.second << endl;
}

G:这题是我开的第一题,其实蛮简单的,可以用很多方法做,我第一眼就想到用map来写

大体题意:给一个树,每个树要连接偶数的节点,可以有重复的线段连接同一个节点,问,要额外加上多少个线段

只要把每个点有多少条线段算出来就行,重要的是0个线段也是偶数,就不需要额外计算,把奇数点求出来塞到一个vector里面应该是很方便的了

void solve() {
    ll n, m,u,v;
    cin >> n >> m;
    vector<ll>a(n + 1, 0),b;
    while (m--) {
        cin >> u >> v;
        a[v]++;
        a[u]++;
    }
    for (int i = 1; i <= n; i++) {
        if (a[i] % 2)b.push_back(i);
    }
    cout << b.size() / 2 << endl;
    for (int i = 0; i < b.size(); i += 2) {
        cout << b[i] << " " << b[i + 1] << endl;
    }
}

H:这题主要的点在于,看到最小化最大值,可以很自然的想到二分答案)

题目大意:有俩排人,要把其中一排人加到另一排里面去,然后每个城市的人有一个不能去的城市,也只有这个城市的人不能去,让一个城市的人去另个城市的f值尽可能小,求出这个尽可能小的最大值

void solve() {
    ll n,sum=0;
    cin >> n;
    vector<ll>a(n + 1), b(n + 1),c(n+1);
    for (int i = 1; i <= n; i++) {
        cin >> a[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> b[i];
        sum += b[i];
    }
    for (int i = 1; i <= n; i++) {
        cin >> c[i];
    }
    auto check = [&](ll e)->bool {
        ll w = 0;
        for (int i = 1; i <= n; i++) {
            w += e / c[i];
        }
        if (sum > w)return false;
        for (int i = 1; i <= n; i++) {
            if (b[i] > w - e / c[a[i]])return false;
        }
        return true;
        };
    ll l = 0, r = inf;
    while (l + 1 < r) {
        ll mid = (l + r) >> 1;
        if (check(mid))r = mid;
        else l = mid;
    }
    cout << r << endl;
}

I:这题就是很基础的签到,注意的是yes和no的正确书写,我赛时给了5发都是因为no和NO

int n,s,t;
    cin>>n>>s>>t;
    vector<int>a(n+1);
    for(int i=1;i<=n;i++) cin>>a[i];
    if(s==t)
    {
        cout<<"Yes\n";
        return;
    }
    if(s>t) swap(s,t);
    if((s>=1&&s<=n&&t>=1&&t<=n)||(s>=n+1&&s<=2*n&&t>=n+1&&t<=2*n)) cout<<(n>=3?"Yes":"No")<<'\n';
    else cout<<(a[s]!=t?"Yes":"No")<<'\n';

K:这题就是简单的挤格子,其关键的或者说有用的格子只有第一个格子,然后算下面有几个相同的就行,值得注意的是如果有2个相同的首位格留下最大的就行

void solve() {
    ll n, m;
    cin >> n >> m;
    ll a[n+1][m+1];
    for (int i = 1; i <= n; i++) {
        for (int j = 1; j <= m; j++) {
            cin >> a[i][j];
        }
    }
    ll q[m + 1];
    for (int i = 1; i <= m; i++)q[i] = 0;
    for (int i = 1; i <= m; i++) {
        ll e = 0;
        for (int j = 1; j <= n; j++) {
            if (a[j][i] == a[1][i])e++;
            else break;
        }
        q[a[1][i]] = max(q[a[1][i]], e);
    }
    ll sum = n*m;
    for (int i = 1; i <= m; i++) {
        sum -= q[i];
       // cout << q[i] << " " << i << endl;
    }
    cout <<  sum << endl;
}

这场感觉就是打的有点烂,a可以学但是感觉到差不多是上线了,b和L应该是可以写的但是我没写(可能)甚至没看,等等就去看看(这是错的)到银牌线了,但是赛时肯定不可能这么顺,最多可能只到铜,希望我郑州之旅顺利吧

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值