牛客周賽 Round 70 A~G 題解

AdviseDY發表於2024-12-07

目錄
  • Preface
  • Problem A. 小苯晨跑
  • Problem B. 小苯過馬路
  • Problem C. 小苯的字串染色
  • Problem D. 小苯的能量項鍊
  • Problem E. 小苯的最短路
  • Problem F. 小苯的優雅好序列
  • Problem G. 小苯的樹上操作
  • PostScript

Preface

感覺\(F\)要比\(G\)難呢,應該是\(F\)沒太多人做了,導致大家沒去開\(G\)吧。

又是鬥志頹廢的一集,最近該忙期末考試了。

我會在程式碼一些有必要的地方加上註釋,簽到題可能一般就不會寫了.

以下是程式碼火車頭:

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <vector>
#include <set>
#include <queue>
#include <map>
#include <unordered_map>
#include <iomanip>
#define endl '\n'
#define int long long
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define rep2(i,a,b) for(int i=(a);i>=(b);i--)
using namespace std;

template<typename T>
void cc(const vector<T> &tem) {
    for (const auto &x: tem) cout << x << ' ';
    cout << endl;
}

template<typename T>
void cc(const T &a) { cout << a << endl; }

template<typename T1, typename T2>
void cc(const T1 &a, const T2 &b) { cout << a << ' ' << b << endl; }

template<typename T1, typename T2, typename T3>
void cc(const T1 &a, const T2 &b, const T3 &c) { cout << a << ' ' << b << ' ' << c << endl; }

void cc(const string &s) { cout << s << endl; }

void fileRead() {
#ifdef LOCALL
    freopen("D:\\AADVISE\\Clioncode\\untitled1\\in.txt", "r", stdin);
    freopen("D:\\AADVISE\\Clioncode\\untitled1\\out.txt", "w", stdout);
#endif
}

void kuaidu() { ios::sync_with_stdio(false), cin.tie(0), cout.tie(0); }

inline int max(int a, int b) {
    if (a < b) return b;
    return a;
}

inline double max(double a, double b) {
    if (a < b) return b;
    return a;
}

inline int min(int a, int b) {
    if (a < b) return a;
    return b;
}

inline double min(double a, double b) {
    if (a < b) return a;
    return b;
}

void cmax(int &a, const int &b) { if (b > a) a = b; }
void cmin(int &a, const int &b) { if (b < a) a = b; }
void cmin(double &a, const double &b) { if (b < a) a = b; }
void cmax(double &a, const double &b) { if (b > a) a = b; }
using PII = pair<int, int>;
using i128 = __int128;
using vec_int = std::vector<int>;
using vec_char = std::vector<char>;
using vec_double = std::vector<double>;
using vec_int2 = std::vector<std::vector<int> >;
using que_int = std::queue<int>;

Problem A. 小苯晨跑

沒什麼好說的,直接上程式碼

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
 
//--------------------------------------------------------------------------------
//struct or namespace:
 
int A[N];
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        rep(i, 1, 4) cin >> A[i];
        rep(i, 1, 4) {
            if (A[i] != A[1]) {
                cc("YES");
                goto Z;
            }
        }
        cc("NO");
    Z:;
 
    }
    return 0;
}
/*
 
 
*/

Problem B. 小苯過馬路

一開始讀錯題了,程式碼寫的很醜...

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
 
//--------------------------------------------------------------------------------
//struct or namespace:
 
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    //cin >> T;
    while (T--) {
        int x, y, k, t;
        cin >> x >> y >> k >> t;
        char c; cin >> c;
        int ans = 0;
        if (c == 'R') {
            ans += k;
            ans += t;
            cc(ans);
            goto Z;
        }
        else {
            ans += k;
            if (t - k <= 0) {
                cc(t);
                goto Z;
            }
            ans += x;
            ans += t;
            cc(ans);
 
        }
 
 
    Z:;
 
    }
    return 0;
}
/*
 
 
*/

Problem C. 小苯的字串染色

感覺很搞笑呢,次數不超過\(n\)次,所以每一次直接選擇長度為\(1\)的就好了,這題是腦筋急轉彎。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
 
//--------------------------------------------------------------------------------
//struct or namespace:
 
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n;
        string s; cin >> s;
 
        vec_int ans;
        rep(i, 0, n - 1) {
            if (s[i] == '1') ans.push_back(i + 1);
        }
        cc(ans.size());
        for (auto& x : ans) cc(x, x);
    }
    return 0;
}
/*
 
 
*/

Problem D. 小苯的能量項鍊

又是讀錯題的一集,怎麼老是讀錯題(bu

要注意去掉項鍊的珠子不算為保留的能量,能量最後只會剩下最多兩個珠子。

可以列舉第\(i\)個珠子是我們剩下的左邊的珠子,那剩下的右邊的珠子就是一個區間內的最大值。\(i=1\)的時候,右邊選擇的區間就是\([n-k,n]\),當\(i\)自增的時候,右邊的區間的左半部分也要自增。有個小注意的地方就是我們選擇的區間不能包括\(i\),所以左半部分還要和\(i+1\)取一個\(max\)

維護區間\(max\)直接\(copy\)了一個封裝的線段樹。

//--------------------------------------------------------------------------------
const int N = 5e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
 
//--------------------------------------------------------------------------------
//struct or namespace:
class SEG {
#define xl x+x
#define xr x+x+1
    //TODO 節點維護資訊、apply函式、up函式
    struct info {
        int mmax = 0;
        void apply(int k) {
            // sum += siz * k;
            mmax += k;
        }
        friend info operator+(const info& q1, const info& q2) {
            info q;
            q.mmax = max(q1.mmax, q2.mmax);
            // q.siz = q1.siz + q2.siz;
            // q.sum = q1.sum + q2.sum;
            return q;
        }
    };
 
    int  L, R;
    info F[unsigned(N * 2.1)];
    void init(int x, int l, int r) {
        if (l == r) { F[x] = info(); return; }
        int mid = l + r >> 1;
        init(xl, l, mid), init(xr, mid + 1, r);
        F[x] = F[xl] + F[xr];
    }
    void add(int x, int l, int r, int l1, int r1, int k) {
        if (l1 > r1) return;
        if (l1 <= l and r <= r1) { F[x].apply(k); return; }
        int mid = l + r >> 1;
        if (r1 <= mid) add(xl, l, mid, l1, r1, k);
        else if (mid < l1) add(xr, mid + 1, r, l1, r1, k);
        else add(xl, l, mid, l1, mid, k), add(xr, mid + 1, r, mid + 1, r1, k);
        F[x] = F[xl] + F[xr];
    }
    info qry(int x, int l, int r, int l1, int r1) {
        if (l1 > r1) return info();
        if (l1 <= l and r <= r1)  return F[x];
        int mid = l + r >> 1;
        if (r1 <= mid) return qry(xl, l, mid, l1, r1);
        else if (mid < l1) return qry(xr, mid + 1, r, l1, r1);
        else { return qry(xl, l, mid, l1, mid) + qry(xr, mid + 1, r, mid + 1, r1); }
    }
#undef xl
#undef xr
public:
    void clear(int l, int r) { L = l, R = r; init(1, l, r); }
    void add(int l, int r, int k) { add(1, L, R, l, r, k); }
    info qry(int l, int r) { return qry(1, L, R, l, r); }
}seg;
int A[N];
//--------------------------------------------------------------------------------
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n >> m;
        seg.clear(1, n);
        rep(i, 1, n) {
            cin >> A[i];
            seg.add(i, i, A[i]);
        }
        int mmax = 0;
        if (m >= n - 2) {
            sort(A + 1, A + n + 1);
            cc(A[n] + A[n - 1]);
            continue;
        }
        rep(i, 1, m + 1) {
            cmax(mmax, A[i] + seg.qry(max(i + 1, n - m + i - 1), n).mmax);
        }
 
        cc(mmax);
 
    }
    return 0;
}
/*
 
 
*/

Problem E. 小苯的最短路

一看題就直接打表找規律了。

定義\(f(x)\)的意思是從\(2\)\(x\)的異或和

\(n\)是奇數的時候,就直接是\(f(n-1)\)\(n\)是偶數的時候就是\(f(n-1)異或(n+1)\)

接下來只要知道\(f\)函式怎麼求就好了。

打表規律得,去掉\(x=1,2\)的情況,剩下的存在迴圈節\(=4\)

依次是\(1,5,0,6\)\(1,9,0,10\)\(1,13,0,14\)

能看出來第一項都是\(1\),第三項都是\(0\),第二項首項是\(5\),每次增\(4\),第四項每次都是第二項\(+1\)

所以寫出來這個規律就好了。

//--------------------------------------------------------------------------------
const int N = 1e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
vector<PII> A[N];
int dis[N];
bool vis[N];
//--------------------------------------------------------------------------------
//struct or namespace:
struct node {
    int to;
    int val;
    bool operator<(const node& q1) const {
        return q1.val < val;
    }
};
//--------------------------------------------------------------------------------
 
int dfs(int x) {
    if (x == 1) return 0;
    if (x == 2) return 2;
    int y = (x - 3) % 4;
    if (y == 0) return 1;
    if (y == 2) return 0;
    int num = (x - 3) / 4 + 1;
    if (y == 1) {
        return (num * 4 + 1);
    }
    else {
        return (num * 4 + 2);
    }
 
 
 
}
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n;
        if (n % 2 == 1) {
            cc(dfs(n));
        }
        else {
            cc(dfs(n - 1) ^ (n + 1));
        }
 
 
    }
    return 0;
}
/*
 
 
*/

Problem F. 小苯的優雅好序列

看題解才過的。

首先能知道經典作差,不變的是兩項的差,所以作差就好了。

然後發現找做差的因數。但是一開始是想的取所有相鄰的數(找相鄰的就可以推廣到所有)的差的\(gcd\),但是之後卻無從下手,因為列舉的因數代表的是加上\(x\)後的數是多少,但是如果取了\(gcd\),就無法知道加上\(x\)的是誰了。(我知道這裡寫的雲裡霧裡,看一下樣例\(1\)就好了)。

所以我們可以找到第一對相鄰的不一樣的數,不需要再看別的了,因為需要保證所有的都滿足,然後就列舉差值的因數,寫一個\(check\)函式正常\(O(n)\)的掃就好了。

\(check\)函式就去判斷相鄰的數字是不是都是有倍數關係的。

時間複雜度是\(O(n*\sqrt(n))\)

//--------------------------------------------------------------------------------
const int N = 5e4 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
 
int A[N];
//--------------------------------------------------------------------------------
//struct or namespace:
namespace ms {
    namespace kuai {
        int kuai(int a, int b) {
            int l = 1;
            while (b) {
                if (b % 2) l = l * a % mod;
                b /= 2;
                a = a * a % mod;
            }
            return l;
        }
 
        int ni(int a) { return kuai(a, mod - 2); }
    }
 
    namespace ni {
        vector<int> fact, infact;
 
        void cal_ni() {
            fact.resize(N + 5);
            infact.resize(N + 5);
            fact[0] = infact[0] = 1;
            infact[1] = 1;
            for (int i = 1; i < N; i++) {
                fact[i] = fact[i - 1] * i % mod;
                if (i >= 2) infact[i] = (mod - mod / i) * infact[mod % i] % mod;
            }
        }
    }
 
    namespace pri {
        vector<int> pri;
        bool ispri[N], biao[N];
 
        void cal_pri() {
            for (int i = 2; i < N; i++) {
                if (biao[i]) continue;
                pri.push_back(i);
                for (int j = i; j < N; j += i) biao[j] = 1;
            }
            for (auto &x: pri) ispri[x] = 1;
        }
    }
 
    namespace gcd {
        int gcd(int a,int b) {
            if (!b) return a;
            return gcd(b, a % b);
        }
    }
}
 
int B[N];
//--------------------------------------------------------------------------------
 
bool check(int add) {
    if (add <= 0 or add > m) return 0;
 
    rep(i, 1, n) {
        B[i] = A[i] + add;
    }
    int fl = 1;
    rep(i, 2, n) {
        if (B[i] % B[i - 1] == 0 or B[i - 1] % B[i] == 0) {
        } else fl = 0;
    }
    return fl;
}
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n >> m;
        rep(i, 1, n) {
            cin >> A[i];
        }
        if (n == 1) {
            cc(m, m * (m + 1) / 2);
            continue;
        }
        using ms::gcd::gcd;
        int x = 0, y = 0;
        rep(i, 1, n-1) {
            if (A[i] != A[i + 1]) {
                x = i, y = i + 1;
                break;
            }
        }
        if (x == 0 and y == 0) {
            cc(m, m * (m + 1) / 2);
            continue;
        }
        if (A[x] > A[y]) swap(x, y);
        int t;
        t = A[y] - A[x];
        int ans = 0;
        int sum = 0;
        for (int i = 1; i * i <= t; i++) {
            if (t % i) continue;
            if (check(i - A[x])) ans++, sum += i - A[x];
            // ans += check(i);
            if (i != t / i and check(t / i - A[x])) ans++, sum += t / i - A[x];
        }
        cc(ans, sum);
    }
    return 0;
}
 
/*
 
 
*/

Problem G. 小苯的樹上操作

其實和\(F\)的難度應該是差距不大的,但是個人更擅長圖上的題一些。

先定義\(dp[x]\)代表以\(x\)的子樹內的最大的答案,\(g\)陣列代表著以\(x\)為根的最大答案。

首先能知道根據這兩個操作推出:

如果我們當前的點的權值是\(>=0\)的,那麼我們\(dp[x]\)一定是子樹的貢獻的求和,即\(\sum dp[y]\)

如果小於\(0\)的時候,那麼我們要麼是當前的點權加上子樹之和,要麼就是選擇子樹的一個最大值。

那麼如何從\(dp\)推廣到\(g\)呢?

如果\(x\)的點權大於等於\(0\),那麼\(g[y]=g[x]\),如果小於呢?當\(g[x]\)是由子樹最大值得出的,我們就需要判斷出子樹最大值是哪個子樹,如果和我們當前的子樹\(y\)一樣,那就需要用子樹次大值推出來,如果不一樣就可以用子樹最大值推出來就好了。

詳細的式子直接看\(dfs\)\(dfs2\)函式就好了。

//--------------------------------------------------------------------------------
const int N = 2e5 + 10;
const int M = 1e6 + 10;
const int mod = 1e9 + 7;
const int INF = 1e16;
int n, m, T;
vec_int A[N];
int to1[N], to2[N], val[N];
int dp[N], g[N];
//--------------------------------------------------------------------------------
//struct or namespace:
 
//--------------------------------------------------------------------------------
 
void dfs(int x,int pa) {
    // int mmax = 0, mmax2 = 0;
    dp[x] = val[x];
    for (auto &y: A[x]) {
        if (y == pa) continue;
        dfs(y, x);
        if (dp[y] >= dp[to1[x]]) to2[x] = to1[x], to1[x] = y;
        else if (dp[y] >= dp[to2[x]]) to2[x] = y;
        dp[x] += dp[y];
    }
    cmax(dp[x], dp[to1[x]]);
}
 
void dfs2(int x,int pa) {
    for (auto &y: A[x]) {
        if (y == pa) continue;
		g[y] = g[x];
        if (val[x] >= 0) g[y] = g[x];
        else {
            if (to1[x] == y) cmax(g[y], dp[y] + dp[to2[x]]);
            else cmax(g[y], dp[y] + dp[to1[x]]);
        }
        dfs2(y, x);
    }
}
 
signed main() {
    fileRead();
    kuaidu();
    T = 1;
    cin >> T;
    while (T--) {
        cin >> n;
        rep(i, 1, n) {
            A[i].clear();
            g[i] = to1[i] = to2[i] = 0;
        }
 
        rep(i, 1, n) cin >> val[i];
        rep(i, 1, n-1) {
            int a, b;
            cin >> a >> b;
            A[a].push_back(b);
            A[b].push_back(a);
        }
        dfs(1, 0);
        g[1] = dp[1];
        dfs2(1, 0);
        int ans = 0;
        rep(i, 1, n) cmax(ans, g[i]);
        // cc(dp[1]);
        cc(ans);
    }
    return 0;
}
 
/*
 
 
*/

PostScript

打算開始\(try\)一下\(atcoder\),還沒有做過。

之前說好的打\(cf\),到現在還沒有開始打。。。

到現在\(cf\)分數都還是\(1k4\)...

相關文章