華中農業大學第十三屆程式設計競賽 題解

Martian148發表於2024-04-14

A - scx 的散文詩句

Solution

正負分開來分別處理,按照絕對值從大到小排序,兩兩匹配

注意:當沒有 \(0\) 且 正數 和 負數 都為奇數,即最後剩下一個正數一個負數時,必須匹配

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void solve() {
    int n; cin >> n;
    vector<ll> a, b;
    int cnt_0 = 0;
    for (int i = 1; i <= n; i++) {
        ll x; cin >> x;
        if (x > 0) a.push_back(x);
        if (x < 0) b.push_back(x);
        if (x == 0) cnt_0 ++;
    }
    ll ans = 0;
    sort(a.begin(), a.end());
    reverse(a.begin(), a.end());
    sort(b.begin(), b.end());
    for (int i = 0; i < a.size(); i += 2) {
        if (i + 1 < a.size())
            ans += a[i] * a[i + 1];
    }
    for (int i = 0; i < b.size(); i += 2) {
        if (i + 1 < b.size())
            ans += b[i] * b[i + 1];
    }
    if (cnt_0 == 0 && a.size() % 2 == 1 && b.size() % 2 == 1) 
        ans += a.back() * b.back();
    cout << ans << '\n';
}

int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int t; cin >> t;
    while (t--) solve();
}

B - 喵喵喵

Solution

我的做法和 std 好像不太一樣

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^n a_ia_j\lfloor \frac{x\gcd(a_i,a_j)+y}{z}\rfloor \]

\(\gcd(a_i,a_j) =d\) 的值確定了之後,\(\lfloor \frac{x\gcd(a_i,a_j)+y}{z}\rfloor=p\) 的值也就確定了

關鍵是 \(a_ia_j\),考慮把 \(a_ia_j\) 變成 \(d^2 \times \frac{a_i}{d}\frac{a_j}{d}\)

我們列舉一個 \(d\) ,式子轉化成

\[\sum\limits_{i=1}^n\sum\limits_{j=1}^n d^2 \times \frac{a_i}{d}\frac{a_j}{d}\times p \]

\[d^2 \times p\times \sum\limits_{d|a_i} \frac{a_i}{d} \sum\limits_{d|a_j} \frac{a_j}{d} \]

這個式子可以使用調和做法求出

但是有可能會算出,比如 \(8,4\) 會在 \(2\) 的地方算一次答案,會在 \(4\) 的地方算一個答案

所以我們對於 \(d\),需要在 \(d\) 的倍數的地方把此時 \(d\) 的貢獻減去

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll TT = 1e9 + 7;
ll x, y, z;
ll read() {
    ll ret = 0; char ch = getchar();
    while (ch < '0' || ch > '9') ch = getchar();
    while (ch <= '9' && ch >= '0') ret = ret * 10 + ch - '0', ch = getchar();
    return ret;
}

void print(ll x) {
    if (x == 0) return ;
    print(x / 10);
    putchar(x % 10 + '0');
}
int main() {
    int n; cin >> n;
    ll ans = 0;
    x = read(), y = read(), z = read();
    vector<ll> a(n + 1);
    for (int i = 1; i <= n; i++)
        a[i] = read();
    ll max_x = *max_element(a.begin() + 1, a.end());
    vector<ll> cnt(max_x + 1, 0), p(max_x + 1, 0);
    for (int i = 1; i <= n; i++)
        p[a[i]]++;
    for (ll d = max_x; d >= 1; d --) {
        ll now = 0, os = (x * d + y) / z;
        ll pre = 0;
        for (int j = 1; j * d <= max_x; j++) {
            pre = (pre + p[j * d] * j) % TT;
        }
        for (int j = 1; j * d <= max_x; j++) {
            now = (now + pre * p[j * d] % TT * j) % TT;
        }
        ll oa = d * d % TT;
        now = oa * now % TT;
        cnt[d] = now;
        for (int j = 2; j * d <= max_x; j++)
            cnt[d] = ((cnt[d] - cnt[j * d]) % TT + TT) % TT;
        ans = (ans + cnt[d] * os) % TT;
    }
    print(ans);
    return 0;
}

C - 貓貓大隊

Solution

把人類看作 \(0\),貓貓狀態看作 \(1\)

使用魔法就可以看成異或操作

Code

#include <bits/stdc++.h>
using  namespace std;
int main() {
    int op = 0;
    int n; cin >> n;
    for (int i = 1; i <= n; i++) {
        int now; cin >> now;
        op ^= now;
    }
    if (op == 0) 
        cout << "zzy" << endl;
    else 
        cout << "miao" << endl;
    return 0;
}

D - 無限的韻律源點

Solution

total best 的維護非常簡單,用一個小根堆維護即可

recent best 的維護較複雜,可以使用 set + 一個大根堆維護 \([i - ra + 1, i]\) 的前 \(rb\) 大值

具體地,先從 \(set\) 中刪去不合法元素,再把 \(a[i]\) 插入大根堆中,再對 set 的最小值和大根堆的對頂進行交換

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pil;
const ll MAXN = 2e5+5;
ll n, b, ra, rb, a[MAXN], flag[MAXN], ans;
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> b >> ra >> rb;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
	}
	priority_queue<ll, vector<ll>, greater<ll> > total;
	set<pil> st;
	priority_queue<pil> op;
	ll sum1 = 0, sum2 = 0;
	for(int i = 1; i <= n; i ++){
		if(total.size() < b || total.top() <= a[i]){
			sum1 += a[i];
			total.push(a[i]);
		}
		while(total.size() > b){
			sum1 -= total.top();
			total.pop();
		}
		
		op.push({a[i], i});
		for(int i = 1; i <= n; i ++){
			
		}
		while(!op.empty() && op.top().second <= max(0ll, i - ra))
			op.pop();
		if(flag[max(0ll, i - ra)]){
			st.erase({a[max(0ll, i - ra)], max(0ll, i - ra)});
			flag[max(0ll, i - ra)] = 0;
			sum2 -= a[max(0ll, i - ra)];
		}
		if(st.size() < rb || st.begin() -> first <= op.top().first){
			st.insert(op.top());
			sum2 += op.top().first;
			flag[op.top().second] = 1;
			op.pop();
		}
		while(st.size() > rb){
			sum2 -= st.begin() -> first;
			flag[st.begin() -> second] = 0;
			op.push(*st.begin());
			st.erase(st.begin());
		}
		ans = max(ans, sum1 + sum2);
	}
	cout << ans << endl;
	return 0;
}

E - 怯戰蜥蜴

Solution

求字首和,在字首和上二分

Code

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll MAXN = 2e5+5;
ll n, q, a[MAXN], s[MAXN], x;
int main(){
	ios::sync_with_stdio(false);
	cin >> n >> q;
	for(int i = 1; i <= n; i ++){
		cin >> a[i];
		s[i] = s[i - 1] + a[i];
	}
	while(q --){
		cin >> x;
		ll idx = upper_bound(s + 1, s + n + 1, x) - s;
		cout << idx - 1 << endl;
	}
	return 0;
}

H - To the Moon, Finding Paradise

Solution

考慮使用傳送器不會造成能量損失,能量 \(w\) 越大,可走的邊越多,\(s-t\) 的體力消耗值越小,滿足單調性

二分答案能量 \(w\),check 使用 \(BFS\) 檢視 \(s-t\) 的最小值,若小於 \(c\) 則 check 成功

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
const ll inf = 1e18;
int main() {
    int N, M, S, T, C; cin >> N >> M >> S >> T >> C;
    vector<ll> V(N + 1, 0);
    for (int i = 1; i <= N; i++) cin >> V[i];
    vector<vector<pii> > g(N + 1, vector<pii>());
    for (int i = 1; i <= M; i++) {
        int u, v, w; cin >> u >> v >> w;
        g[u].push_back({v, w});
    }
    
    auto check = [&] (int W) {
        vector<ll> dis(N + 1, inf);
        priority_queue<pii, vector<pii>, greater<pii> > pq;
        pq.push({V[S], S}); dis[S] = V[S];
        while (!pq.empty()) {
            auto [d, u] = pq.top(); pq.pop();
            if (d != dis[u]) continue;
            for (auto [v, w] : g[u]) {
                if (w > W) continue;
                if (dis[v] > d + V[v]) {
                    dis[v] = d + V[v];
                    pq.push({dis[v], v});
                }
            }
        }
        return dis[T] <= C;
    };
    
    int l = -1, r = 1e9;
    while (l + 1 < r) {
        int mid = l + r >> 1;
        if (check (mid)) r = mid;
        else l = mid;
    }
    cout << r << endl;
    return 0;
}

I - fumo 星

Solution

列舉兩個點生成一條直線

最後把所有直線排序後去重即可

Code

#include <bits/stdc++.h>
using namespace std;
const double eps = 1e-8;
const int inf = 0x3f3f3f3f;

int cmp (double A, double B) {
    if (fabs(A - B) < eps) return 0;
    if (A < B) return -1;
    else return 1;
}

struct Node {
    double x, y;
    Node() {x = 0; y = 0;}
    bool operator <(const Node B) const {
        return x < B.x || (x == B.x && y < B.y);
    }
};

struct Line {
    double k, b;
    Line() {k = 0; b = 0;}
    Line(Node A, Node B) {
        if (A.x == B.x) {
            k = inf; b = A.x;
        }
        else {
            k = (A.y - B.y) / (A.x - B.x);
            b = A.y - k * A.x;
        }
    }
    bool operator <(const Line B) const {
        return k < B.k || (k == B.k && b < B.b);
    }
};

int main() {
    int n; cin >> n;
    vector<Node> a(n);
    for (int i = 0; i < n; i++) {
        cin >> a[i].x >> a[i].y;
    }
    sort (a.begin(), a.end());
    for (int i = 1; i < n; i++) {
        if (cmp(a[i].x, a[i - 1].x) == 0 && cmp (a[i].y, a[i - 1].y) == 0) {
            cout << "inf" << endl;
            return 0;
        }
    }
    vector<Line> p;
    for (int i = 0; i < n; i++)
        for (int j = i + 1; j < n; j++) {
            p.push_back(Line(a[i], a[j]));
        }
    int cnt = 0;
    sort (p.begin(), p.end());
    for (int i = 1; i < p.size(); i++) {
        if (cmp(p[i].k, p[i - 1].k) == 0 && cmp(p[i].b, p[i - 1].b) == 0)
            cnt ++;
    }
    cout << p.size() - cnt << endl;
    return 0;
}

J - 上春山

Solution

當高度為 \(k\) 時,答案為

\[\sum\limits_{h_i<k} h_i-(a_i-k) \]

按照 \(h_i\) 從大到小排序後,\(\sum h_i\)\(\sum a_i\) 可以求出來,\(\sum k=k\times j\) 其中 \(j\) 表示 \(h_i\)\(k\) 大的個數

由於每部分都說獨立的,所以 \(k\) 越大越好,也就是取當前最矮的 \(h_i -1\)

就這樣列舉找出最大值即可

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;

struct Node {
    ll h, a;
    bool operator < (const Node B) const {
        return h > B.h || (h == B.h && a < B.a);
    }
};

ll ans = 0;
int main() {
    ios::sync_with_stdio(0);
    cin.tie(0);
    int n;cin >> n;
    vector<Node> p(n);
    for (int i = 0; i < n; i++)
        cin >> p[i].h;
    for (int i = 0; i < n; i++)
        cin >> p[i].a;
    sort(p.begin(), p.end());
    ll sum_h = 0, sum_a = 0;
    for (int i = 0; i < n;) {
        int j = i;
        while (j < n && p[j].h == p[i].h) {
            sum_h += p[j].h;
            sum_a += p[j].a;
            j++;
        }
        ll k = p[i].h - 1;
        ll now = sum_a - sum_h + k * j;
        ans = max (ans, now);
        i = j;
    }
    cout << ans << endl;
    return 0;
}

L - 南湖少年團

Solution

直接使用 substr 截下來字串即可

Code

#include <bits/stdc++.h>
using namespace std;
int main() {
    int n, m;
    cin >> n >> m;
    string s1, s2; cin >> s1 >> s2;
    int ans = 0;
    for (int i = 0; i + s1.size() - 1 < s2.size(); i++) {
        if (s2.substr(i, s1.size()) == s1)
            ans ++;
    }
    cout << ans << endl;
    return 0;
}

M - 上帝造題的八分鐘

Solution

數位 DP 板子題

定義 \(dp[pos][cp][pre1][pre2][limit][lead]\)

表示列舉到 \(pos\) 位置,差值位 \(cp\),前一個位置為 \(pre1\),前兩個位置為 \(pre2\),最大限制和前導零限制

Code

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll TT = 1e9 + 7;

ll dp[104][205][11][11][2][2][2];

ll dfs(int pos, int cp, int pre1, int pre2, int limit, int n3, int lead, vector<int> &num) {
    ll ret = 0;
    if (pos == -1)
        return n3 && (abs (cp - 100) >= 1);
    if (~ dp[pos][cp][pre1][pre2][limit][n3][lead])
        return dp[pos][cp][pre1][pre2][limit][n3][lead];
    int up = (limit ? num[pos] : 9);
    for (int i = 0; i <= up; i++) {
        if (lead && i == 0)
            ret += dfs (pos - 1, cp, i, pre1, limit && (i == up), n3 , 1, num);
        else 
            ret += dfs (pos - 1, cp + (i == 0) - (i == 1), i, pre1, limit && (i == up), n3 | (i == 3 && pre1 == 3 && pre2 == 3), 0, num);
        ret %= TT;
    }
    return dp[pos][cp][pre1][pre2][limit][n3][lead] = ret;
}

ll solve(vector<int> p) {
    memset(dp, -1, sizeof dp);
    return dfs (p.size() - 1, 100, -1, -1, 1, 0, 1, p);
}

int check (string s) {
    int op = 0, op2 = 0;
    for (int i = 0; i < s.size(); i++) {
        if (s[i] == '1') op ++;
        if (s[i] == '0') op --;
    }
    for (int i = 0; i + 3 - 1 < s.size(); i++) {
        if (s.substr(i, 3) == (string)"333")
            op2 |= 1;
    }
    op = (abs (op) >= 1);
    return op && op2;
}

int main() {
    string l, r; cin >> l >> r;
    vector<int> L, R;
    for (int i = 0; i < l.size(); i++)
        L.push_back(l[i] - '0');
    for (int i = 0; i < r.size(); i++)
        R.push_back(r[i] - '0');

    reverse(R.begin(), R.end());
    reverse(L.begin(), L.end());
    ll ans = ((solve(R) - solve(L) + check(l)) % TT + TT) % TT;
    cout << ans << endl;
    return 0;
}

相關文章