Educational Codeforces Round 38 部分題解

D. Buy a Ticket

分析

建一個源點,連向全部結點,邊的花費爲那個結點的花費,圖中原有的邊花費翻倍,最後跑一遍最短路便可。c++

code

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

const int N = 2e5 + 10;
const long long INF = 2e18;
struct Edge { int to; long long w; };
vector<Edge> G[N];
long long d[N];
priority_queue<pair<long long, int>, vector<pair<long long, int> >, greater<pair<long long, int> > > q;
int main() {
    int n, m;
    scanf("%d%d", &n, &m);
    for(int i = 0; i < m; i++) {
        int u, v;
        long long w;
        scanf("%d%d%I64d", &u, &v, &w);
        G[u].push_back(Edge{v, 2 * w});
        G[v].push_back(Edge{u, 2 * w});
    }
    for(int i = 0; i < n; i++) {
        long long x;
        scanf("%I64d", &x);
        G[0].push_back(Edge{i + 1, x});
    }
    fill(d, d + N, INF);
    d[0] = 0;
    q.push(pair<long long, int>(0, 0));
    while(!q.empty()) {
        pair<long long, int> now = q.top(); q.pop();
        if(d[now.second] < now.first) continue;
        for(Edge e : G[now.second]) {
            if(now.first + e.w < d[e.to]) {
                d[e.to] = now.first + e.w;
                q.push(pair<long long, int>(d[e.to], e.to));
            }
        }
    }
    for(int i = 1; i <= n; i++) printf("%I64d%c", d[i], " \n"[i == n]);
    return 0;
}

E. Max History

分析

推公式。
過程這裏很詳細了。spa

code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e6 + 1;
const int MOD = 1e9 + 7;
int a[N];
long long qpow(long long x, long long k) {
    long long res = 1;
    while(k) {
        if(k & 1) res = res * x % MOD;
        x = x * x % MOD;
        k >>= 1;
    }
    return res;
}
int main() {
    int n;
    scanf("%d", &n);
    for(int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }
    sort(a, a + n);
    long long x = 1, ans = 0;
    for(int i = 0; i < n; i++) x = x * (i + 1) % MOD;
    for(int i = 0; i < n - 1; i++) {
        if(a[i] == a[n - 1]) break;
        ans = (ans + (a[i] * x % MOD) * qpow(n - (lower_bound(a, a + n, a[i]) - a), MOD - 2) % MOD) % MOD;
    }
    cout << ans << endl;
    return 0;
}

F. Erasing Substrings

分析

這道題很神奇。code

\(n\) 爲字符串長, \(m\) 爲不超過 \(n\) 的最大的 \(2\) 的倍數。blog

那麼最後答案長度爲 \(n-m+1\),組成答案的這些字符中,第一個字符必定是在原子符串區間 \([0,m-1]\) 內取到的,第二個在區間 \([1,m]\) 內取到的,後面同理。ci

\(f[i]\)\(1\) 則表示能夠轉移到 \(i\) 這個狀態,其中 $ 0\leq i < m$,二進制位爲 \(1\) 對應某個長度的刪除操做已經執行過了。字符串

每次右移一個字符,而後咱們選擇要轉移到的狀態,由於前面已經有一個字符成爲答案中的一個字符了,因此比較的時候應該是 \(s[i + j]\) 而不是 \(s[j]\)get

每次咱們都要更新出下次能夠轉移到的合法狀態,若是如今的狀態是 \(1001\) ,即前面已經刪過了長度爲 \(1\)\(8\) 的子串,咱們能夠轉移到 \(1001, 1101, 1011, 1111\) ,即咱們能夠選擇刪掉長度爲 \(2\)\(4\) 的子串,或者不刪,直接取接下來的字符。string

code

#include<bits/stdc++.h>
using namespace std;
const int N = 1e5 + 10;
int f[N], g[N];
char ans[N];
int main() {
    string s;
    cin >> s;
    int n = s.length();
    int m = 1;
    while(m <= n) m <<= 1; m >>= 1;
    for(int i = 0; i < m; i++) f[i] = 1;
    for(int i = 0; i <= n - m; i++) {
        ans[i] = 'z';
        for(int j = 0; j < m; j++) {
            if(f[j] && s[i + j] < ans[i]) ans[i] = s[i + j];
        }
        memset(g, 0, sizeof g);
        for(int j = 0; j < m; j++) {
            if(!g[j] && f[j] && ans[i] == s[i + j]) {
                int w = m - 1 - j;
                for(int k = w; k; k = w & (k - 1)) g[m - 1 - k] = 1;
                g[m - 1] = 1;
            }
        }
        for(int j = 0; j < m; j++) f[j] = g[j];
    }
    ans[n - m + 1] = 0;
    printf("%s", ans);
    return 0;
}
相關文章
相關標籤/搜索