1. 程式人生 > 實用技巧 >AtCoder Grand Contest 001 題解

AtCoder Grand Contest 001 題解

A-BBQ Easy (排序)

題意

題解

排序後取奇數位置的數即可。

#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e5;
int arr[N];
typedef long long ll;
int main() {
    int n;
    cin >> n;
    for(int i = 1; i <= 2 * n; i++) {
        cin >> arr[i];
    }
    sort(arr + 1 , arr + 2 * n + 1);
    ll tot = 0;
    for(int i = 1; i <= 2 * n; i += 2) {
        tot += arr[i];
    }
    cout << tot << endl;
}

B-Mysterious Light (gcd)

題意

題解

手動模擬一下發現就是一個求n-x和x的gcd的過程。

ll solve(ll a, ll b) {
    if(!b) {
        return a;
    }
    ll res = 0;
    res += ((a - 1) / b) * 2 * b;
    res += solve(b, a % b);
    return res;
}
 
int main() {
    IOS;
    ll n, x;
    cin >> n >> x;
    cout << solve(n - x, x) + n << endl;
}

C-Shorten Diameter (樹分治,暴力dfs)

題意

給你一顆無根樹,要求刪除最少結點使得剩餘的圖還是一顆樹且直徑不大於K。

題解

由於樹直徑的中點是唯一的,且每個點到這個點的距離不超過直徑的一半(顯然)。
直徑的中點可能在點上(直徑為偶數)也可能在邊上(直徑為奇數)。一個暴力的做法是根據K的奇偶來列舉最終剩餘的樹的直徑中點,刪除距離該點超過K/2的所有點,取其中最小。複雜的$$O(n^2)$$
由於本題資料範圍小,可以暴力。若N非常大,要用到樹分治。


const int N = 3e5 + 10;
const double eps = 1e-5;
 
vector<int> np[N];
typedef pair<int, int> PII;
 
 
int n, k;
int dfs(int p, int fa, int dep) {
    int res = 0;
    if(dep > k / 2) res++;
    for(int nt : np[p]) {
        if(nt == fa) continue;
        res += dfs(nt, p, dep + 1);
    }
    return res;
}
 
int main() {
    IOS;
    cin >> n >> k;
    
    for(int i = 1; i < n; i++) {
        int u, v;
        cin >> u >> v;
        np[u].push_back(v);
        np[v].push_back(u);
    }
    int ans = n - 1;
    if(k % 2 == 0) {
        for(int i = 1; i <= n; i++) { //列舉點
            int tot = 0;
            ans = min(ans, dfs(i, 0, 0));
        }
    } else {
        for(int i = 1; i <= n; i++) { //列舉邊,直徑中心為邊的中心
            int tmp = 0;
            for(int j : np[i]) {
                ans = min(ans, dfs(i, j, 0) + dfs(j, i, 0));
            }
        }
    }
    cout << ans << endl;
}

D-Arrays and Palindrome (思維)

題意

題目比較繞,詳見洛谷的翻譯

題解

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 3e5 + 10;
const double eps = 1e-5;

int arr[N];

int main() {
    IOS;
    int n, m;
    cin >> n >> m;
    int cnt = 0;
    int odd1 = 0, odd2 = 0;
    for(int i = 1; i <= m; i++) {
        cin >> arr[i];
        if(arr[i] % 2) {
            cnt++;
            if(cnt == 1) odd1 = i;
            if(cnt == 2) odd2 = i;
        }
    }
    if(m == 1) {
        cout << arr[1] << endl;
        if(arr[1] == 1) {
            cout << 1 << endl;
            cout << 1 << endl;
        } else {
            cout << 2 << endl;
            cout << 1 << " " << arr[1] - 1 << endl;
        }
    }
    else if(cnt > 2) cout << "Impossible" << endl;
    else {
        if(cnt == 2) {
            swap(arr[odd1], arr[1]);
            swap(arr[odd2], arr[m]);
        } else if(cnt == 1) {
            swap(arr[odd1], arr[1]);
        }
        for(int i = 1; i <= m; i++) cout << arr[i] << " \n"[i == m];
        if(arr[m] - 1) cout << m << endl;
        else cout << m - 1 << endl;
        cout << arr[1] + 1 << " ";
        for(int i = 2; i < m; i++) {
            cout << arr[i] << " ";
        }
        if(arr[m] - 1) cout << arr[m] - 1 << endl;
        else cout << endl;
    }    
}

E-BBQ Hard (dp)

題意

中文題意

題解

組合數\(\left( \begin{matrix} x+y \\ x \end{matrix} \right)\)幾何意義是從\((0, 0)\)\((x, y)\)的路徑數。

略證:

\(\left( \begin{matrix} x+y \\ x \end{matrix} \right) = \left( \begin{matrix} x+(y-1) \\ x \end{matrix} \right)+\left( \begin{matrix} (x-1)+y \\ x-1 \end{matrix} \right)\)

故題目可轉換為從\((0, 0)\)\((a_i+a_j, b_i+b_j)\)的路徑數,進一步可轉化為\((-a_i, -b_i)\)\((a_j, b_j)\)。令每個\((-a_i, -b_i)\)為1,然後dp。最後答案為每個\((a_i, b_i)\)的和。還要減去每個\((-a_i, -b_i)\)\((a_i, b_i)\)的值,即\(\left( \begin{matrix} 2(a_i+b_i) \\ a_i+b_i \end{matrix} \right)\),然後除以2,因為題目要求的是\(i<j\)的數對。

#include <bits/stdc++.h>

#define endl '\n'
#define IOS std::ios::sync_with_stdio(0); cin.tie(0); cout.tie(0)
#define FILE freopen(".//data_generator//in.txt","r",stdin),freopen("res.txt","w",stdout)
#define FI freopen(".//data_generator//in.txt","r",stdin)
#define FO freopen("res.txt","w",stdout)
#define mp make_pair
#define seteps(N) fixed << setprecision(N) 
typedef long long ll;

using namespace std;
/*-----------------------------------------------------------------*/

ll gcd(ll a, ll b) {return b ? gcd(b, a % b) : a;}
#define INF 0x3f3f3f3f

const int N = 2e3 + 10;
const int M = 1e9 + 7;
const double eps = 1e-5;

inline ll qpow(ll a, ll b, ll m) {
    ll res = 1;
    while(b) {
        if(b & 1) res = (res * a) % m;
        a = (a * a) % m;
        b = b >> 1;
    }
    return res;
}

ll inv(ll x) {
    return qpow(x, M - 2, M);
}

ll f[N << 2], rf[N << 2];
ll dp[N << 1][N << 1];

ll C(int n, int m) {
    return f[n] * rf[n - m] % M * rf[m] % M;
}
typedef pair<int, int> PII;
PII arr[300000];

int main() {
    IOS;
    f[0] = rf[0] = 1;
    for(int i = 1; i < (N << 2); i++) {
        f[i] = (f[i - 1] * i) % M;
        rf[i] = (rf[i - 1] * inv(i)) % M;
    }
    int n;
    cin >> n;
    for(int i = 1; i <= n; i++) {
        int a, b;
        cin >> a >> b;
        arr[i] = mp(a, b);
        dp[N - a][N - b]++;
    }    
    for(int i = 1; i < (N << 1); i++) {
        for(int j = 1; j < (N << 1); j++) {
            dp[i][j] = (dp[i][j] + (dp[i - 1][j] + dp[i][j - 1]) % M) % M;
        }
    }
    ll ans = 0;
    for(int i = 1; i <= n; i++) {
        int a = arr[i].first, b = arr[i].second;
        ans += dp[N + a][N + b];
        ans %= M;
        ans -= C(2 * a + 2 * b, 2 * a);
        ans = (ans % M + M) % M;
    }
    ans = ans * 500000004 % M;
    cout << ans << endl;
}

F-Wide Swap (線段樹,拓撲排序)

題意

中文題意

題解

\(P_i\)為下標,i為值構造新序列(即逆序列),題目變為若相鄰的數相差大於K則可交換,求最小字典序。這個轉換一下子把題目變得順眼起來。
為了字典序最小,小的值要越前越好。因此從1開始,如果合法(前面的數比自己大K)就不停向前移動,直到移不動為止。那麼當前1和它前面的數(設為x)相對位置就固定不動了,而且這個位置關係是最優的,然後x向1連邊,代表x在1之前,然後更新1的值\(P_1\)為INF。以此類推,建出的圖優先佇列拓撲排序一下即可。
注意最後的序列還要在逆一次變回原序列對應的答案。

複雜的\(O(n^2)\),但網上正解複雜為\(O(n)\)。以後有時間再補。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <vector>
#include <queue>
using namespace std;
const int N = 5e5 + 10;
const int INF = 0x3f3f3f3f;
typedef long long ll;

int mi[N << 2];
int pe[N], rpe[N];
int n;
int deg[N];
vector<int> np[N];

void build(int l, int r, int rt) {
    if(l == r) {
        mi[rt] = rpe[l];
        return ;
    }
    mi[rt] = INF;
    int mid = (l + r) / 2;
    build(l, mid, rt << 1);
    build(mid + 1, r, rt << 1 | 1);
    mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]);
}

int query(int l, int r, int  L, int  R, int  rt) {
    if(l >= L && r <= R) {
        return mi[rt];
    }
    int mid =(l + r) / 2;
    int res = INF;
    if(L <= mid) res = min(res, query(l, mid, L, R, rt << 1));
    if(R > mid) res = min(res, query(mid + 1, r, L, R, rt << 1 | 1));
    mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]);
    return res;
}

void update(int l, int r, int p, int val, int rt) {
    if(l == r) {
        mi[rt] = val;
        return ;
    }
    int mid = (l + r) / 2;
    if(p <= mid) update(l, mid, p, val, rt << 1);
    else update(mid + 1, r, p, val , rt << 1 | 1);
    mi[rt] = min(mi[rt << 1], mi[rt << 1 | 1]);
}

int find(int p, int v, int k) {
    int l = 1, r = p - 1;
    while(l <= r) {
        int mid = (l + r) / 2;
        int tar = query(1, n, mid, p - 1, 1);
        if(tar >= v + k) {
            r = mid - 1;
        } else {
            l = mid + 1;
        }
    }
    l--;
    return l;
}

vector<int> ans;
priority_queue<int, vector<int>, greater<int> > q;

int main() {
    int k;
    cin >> n >> k;
    for(int i = 1; i <= n; i++) {
        cin >> pe[i];
        rpe[pe[i]] = i;
    }
    build(1, n, 1);
    for(int i = 1; i <= n; i++) {
        int p = find(pe[i], i, k);
        if(p) {
            np[rpe[p]].push_back(i);
            deg[i]++;
        }
        update(1, n, pe[i], INF, 1);
    }
    
    for(int i = 1; i <= n; i++) {
        if(!deg[i]) {
            q.push(i);
        }
    }
    while(!q.empty()) {
        int cur = q.top();
        q.pop();
        ans.push_back(cur);
        for(int nt : np[cur]) {
            deg[nt]--;
            if(!deg[nt]) q.push(nt);
        }
    }
    for(int i = 0; i < ans.size(); i++) {
        pe[ans[i]] = i + 1;
    }
    for(int i = 1; i <= n; i++) cout << pe[i] << endl;
}