zoukankan      html  css  js  c++  java
  • 2020牛客暑期多校训练营(第一场)

    Contest Info


    传送门

    Solved A B C D E F G H I J
    5 / 10 Ø - - - - O - Ø Ø O
    • O 在比赛中通过
    • Ø 赛后通过
    • ! 尝试了但是失败了
    • - 没有尝试

    Solutions


    A. B-Suffix Array

    题意:
    给定一个只包含(a,b)的字符串,现有一函数(B),字符串通过(B)作用能得到一个新的数组(b),其中(displaystyle b_i=min_{1leq j<i,t_j=t_i}(i-j))
    现在找到一个排列(p),使得(B(t_{p_i...p_n})<B_{p_{i+1}...p_n},1leq i<n)都成立,这里是进行字典序比较。

    思路:
    题解的做法是关于一个结论,不过较难想到,这里说一下一种比较容易想的做法:

    • 首先我们得到(b)数组。
    • 对于所有的后缀(i...n),我们找到(R[i])表示当前后缀第(2)(0)的位置,比如后缀以(aaaab)开头时(R[i]=5)
    • 那么显然可以按照(R[i]-i)从小到大排序,对于(R[i]-i=R[j]-j)相等的情况,我们只需要按照(j+1...n)后缀从小到大排序即可。

    容易证明这样排序得到的顺序符合条件。
    细节见代码:

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/15 14:46:06
    #include<bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 1e5 + 5;
     
    struct SA{                                       //sa:1...n  Rank:0...n-1
        int sa[N], c[N], Rank[N], t1[N], t2[N];
        int n;                                          //length
        void da(int* s, int m){
            s[++n] = 0;    //!!  s[n]=0
            int* x = t1, *y = t2;
            for(int i = 0; i < m; ++i) c[i] = 0;
            for(int i = 0; i < n; ++i) c[x[i] = s[i]]++;
            for(int i = 1; i < m; ++i) c[i] += c[i - 1] ;
            for(int i = n - 1; i >= 0; i--) sa[--c[x[i]]] = i;
            for(int k = 1; k <= n; k <<= 1) {
                int p = 0 ;
                for(int i = n - k; i < n; ++i) y[p++] = i ;
                for(int i = 0; i < n; ++i) if(sa[i] >= k) y[p++] =sa[i] - k;
                for(int i = 0; i < m; ++i) c[i] = 0;
                for(int i = 0; i < n; ++i) c[x[y[i]]]++;
                for(int i = 1; i < m; ++i) c[i] += c[i - 1];
                for(int i = n - 1; i >= 0; i--) sa[--c[x[y[i]]]] = y[i] ;
                swap(x , y); p = 1; x[sa[0]] = 0;
                for(int i = 1; i < n; ++i)
                    x[sa[i]] = y[sa[i - 1]] == y[sa[i]] && y[sa[i-1] + k] == y[sa[i] + k] ? p - 1 : p++;
                if(p >= n) break ;
                m = p;
            }
            n--;
            for(int i = 0; i <= n; ++i) Rank[sa[i]] = i;
        }
    }suf;
     
    int n;
    char s[N];
    int R[N], a[N];
     
    void run() {
        suf.n = n;
        for (int i = 0, j; i < n; i = j + 1) {
            j = i;
            while (j + 1 < n && s[j + 1] == s[j]) ++j;
            for (int k = i; k <= j; k++) {
                R[k] = j + 1;
            }
        }
        int pre[2] = {-1, -1};
        for (int i = 0; i < n; ++i) {
            if (pre[s[i] - 'a'] == -1) {
                a[i] = 0;
            } else {
                a[i] = i - pre[s[i] - 'a'];
            }
            pre[s[i] - 'a'] = i;
        }
     
        suf.da(a, n + 2);
    
        vector<int> ord(n);
        iota(all(ord), 0);
        auto get = [&] (int i) {
            return i >= n ? -1 : suf.Rank[i];
        };
         
        sort(all(ord), [&] (int i, int j) {
            int len1 = R[i] - i, len2 = R[j] - j;
            if (len1 != len2) {
                return len1 < len2;
            }
            if (R[i] >= n || R[j] >= n) {
                return R[i] >= n;
            }
            return get(R[i] + 1) < get(R[j] + 1);
        });
     
        for (int i = 0; i < n; ++i) {
            printf("%d", ord[i] + 1);
            if (i < n - 1) printf(" ");
            else printf("
    ");
        }
    }
    int main() {
        while (scanf("%d%s", &n, s) == 2) run();
        return 0;
    }
    

    F. Infinite String Comparision

    将两个串倍长比较即可,其实只需要比较到(|a|+|b|-gcd(|a|,|b|))即可。

    Code
    #include<bits/stdc++.h>
    using namespace std;
    const int maxn=1e5+233;
     
    long long n,k,t;
    int main(){
    string a,b;
        while(cin>>a>>b){
            int i=0,j=0;
            int len1=a.size();
            int len2=b.size();
            int g=__gcd(len1,len2);
            if(len1==len2){
                if(a==b) cout<<"="<<endl;
                if(a>b) cout<<">"<<endl;
                if(a<b) cout<<"<"<<endl;
                continue;
            }
            int f = 0;
            int maxlen=10*max(len1, len2);
            while(maxlen--){
                if(a[i]>b[j]) {
                    f=1;
                    break;
                }
                if(a[i]<b[j]){
                    f=2;
                    break;
                }
                i++;
                j++;
                i%=len1;
                j%=len2;
            }
            if(f==1) cout<<">"<<endl;
            if(f==2) cout<<"<"<<endl;
            if(!f) cout<<"="<<endl;
        }
     
        return 0;
    }
    

    H. Minimum-cost Flow

    因为给出的就是一个网络,具有网络的性质。
    所以我们将网络看作容量为单位(1)的网络,因为最多增广(O(m))次,预处理关于最小费用的前缀和即可。
    询问时找到最小增广次数过后可以直接(O(1))计算。
    注意分子分母的计算,分母为(v),而分子都应乘上(u)

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/12 14:24:44
    #include<bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 100 + 5, M = 200 + 5;
    
    #define INF 0x3f3f3f3f
    struct E {
        int from, to, cp, v;
        E() {}
        E(int f, int t, int cp, int v) : from(f), to(t), cp(cp), v(v) {}
    };
    int f[M];
    struct MCMF {
        int n, m, s, t;
        vector<E> edges;
        vector<int> G[N];
        bool inq[N];
        int d[N], p[N], a[M];
    
        void init(int _n, int _s, int _t) {
            n = _n; s = _s; t = _t;
            for(int i = 0; i <= n; i++) G[i].clear();
            edges.clear(); m = 0;
        }
    
        void addedge(int from, int to, int cap, int cost) {
            edges.emplace_back(from, to, cap, cost);
            edges.emplace_back(to, from, 0, -cost);
            G[from].push_back(m++);
            G[to].push_back(m++);
        }
    
        bool BellmanFord(int &flow, int &cost) {
            for(int i = 0; i <= n; i++) d[i] = INF;
            memset(inq, 0, sizeof inq);
            d[s] = 0, a[s] = INF, inq[s] = true;
            queue<int> Q; Q.push(s);
            while (!Q.empty()) {
                int u = Q.front(); Q.pop();
                inq[u] = false;
                for (int& idx: G[u]) {
                    E &e = edges[idx];
                    if (e.cp && d[e.to] > d[u] + e.v) {
                        d[e.to] = d[u] + e.v;
                        p[e.to] = idx;
                        a[e.to] = min(a[u], e.cp);
                        if (!inq[e.to]) {
                            Q.push(e.to);
                            inq[e.to] = true;
                        }
                    }
                }
            }
            if (d[t] == INF) return false;
            flow += a[t];
            cost += a[t] * d[t];
            int u = t;
            while (u != s) {
                edges[p[u]].cp -= a[t];
                edges[p[u] ^ 1].cp += a[t];
                u = edges[p[u]].from;
            }
            return true;
        }
    
        void go(int& flow) {
            flow = 0;
            int cost = 0;
            while (BellmanFord(flow, cost)) {
                f[flow] = f[flow - 1] + cost;
                cost = 0;
            };
        }
    } MM;
    
    int n, m;
    
    void run() {
        MM.init(n, 1, n);
        for (int i = 0; i < m; i++) {
            int u, v, w; cin >> u >> v >> w;
            MM.addedge(u, v, 1, w);
        }
        int npath;
        MM.go(npath);
        int q; cin >> q;
        while (q--) {
            int u, v; cin >> u >> v;
            if (u == 0) {
                cout << "NaN" << '
    ';
                continue;
            }
            int need = (v + u - 1) / u;
            if (need > npath) {
                cout << "NaN" << '
    ';
            } else {
                assert(u > 0 && v >= u);
                assert(need > 0);
                ll fz = 1ll * f[need - 1] * u + 1ll * (f[need] - f[need - 1]) * (v - (need - 1) * u);
                ll fm = v;
                ll g = __gcd(fz, fm);
                assert(g > 0);
                fz /= g, fm /= g;
                cout << fz << "/" << fm << '
    ';
            }
        }
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while (cin >> n >> m) run();
        return 0;
    }
    

    I. 1 or 2

    题意:
    给出一张(n)个点(m)条边的无向图,回答是否能找到一个子图使得每个点的度数都恰好为(d_i)

    思路:
    是一道(hduoj)上的原题,做法很巧妙。
    (d_i)都为(1),那么这便是一个裸的一般图最大匹配。
    考虑(d_i>1)的情况,其实可以得到启示:对度数进行拆点。
    这样对于两个点(u,v),将他们按照度数拆为若干个点,若现在存在一条边连接(u,v),那么一端连接所有的(u),一端连接所有的(v)即可。
    最后在匹配中,因为是最大匹配,那么只会有两种情况:

    • 两端的边各选一条,这种即是我们选择((u,v))这条边保留,对应度数减少(1)
    • 选择中间的边,这即是在图中删除这条边,度数不发生变化。

    所以正确性就比较显然了。
    细节见代码:

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/12 20:50:24
    #include<bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    //head
    const int N = 500 + 5, M = 100 + 5;
    
    int n, m;
    int d[N];
    
    struct mf {
        // Time complexity: O(n^3)
        // 1-based Vertex index
        // match[x]: vertex matched with x
        // N: numbers of vertex
        int vis[N], par[N], orig[N], match[N], aux[N], t, n;
        vector<int> conn[N];
        queue<int> Q;
        void add_edge(int u, int v) {
            conn[u].push_back(v);
            conn[v].push_back(u);
        }
        void init(int _n) {
            n = _n;
            t = 0;
            for (int i = 0; i <= n; ++i) {
                conn[i].clear();
                match[i] = aux[i] = par[i] = 0;
            }
        }
        void augment(int u, int v) {
            int pv = v, nv;
            do {
                pv = par[v];
                nv = match[pv];
                match[v] = pv;
                match[pv] = v;
                v = nv;
            } while (u != pv);
        }
        int lca(int v, int w) {
            ++t;
            while (true) {
                if (v) {
                    if (aux[v] == t) return v;
                    aux[v] = t;
                    v = orig[par[match[v]]];
                }
                swap(v, w);
            }
        }
        void blossom(int v, int w, int a) {
            while (orig[v] != a) {
                par[v] = w;
                w = match[v];
                if (vis[w] == 1) Q.push(w), vis[w] = 0;
                orig[v] = orig[w] = a;
                v = par[w];
            }
        }
        bool bfs(int u) {
            fill(vis + 1, vis + 1 + n, -1);
            iota(orig + 1, orig + n + 1, 1);
            Q = queue<int>();
            Q.push(u);
            vis[u] = 0;
            while (!Q.empty()) {
                int v = Q.front();
                Q.pop();
                for (int x : conn[v]) {
                    if (vis[x] == -1) {
                        par[x] = v;
                        vis[x] = 1;
                        if (!match[x]) return augment(u, x), true;
                        Q.push(match[x]);
                        vis[match[x]] = 0;
                    } else if (vis[x] == 0 && orig[v] != orig[x]) {
                        int a = lca(orig[v], orig[x]);
                        blossom(x, v, a);
                        blossom(v, x, a);
                    }
                }
            }
            return false;
        }
        int Match() {
            int ans = 0;
            // find random matching (not necessary, constant improvement)
            vector<int> V(n - 1);
            iota(V.begin(), V.end(), 1);
            shuffle(V.begin(), V.end(), mt19937(61471));
            for (auto x : V)
                if (!match[x]) {
                    for (auto y : conn[x])
                    if (!match[y]) {
                        match[x] = y, match[y] = x;
                        ++ans;
                        break;
                    }
                }
            for (int i = 1; i <= n; ++i)
                if (!match[i] && bfs(i)) ++ans;
            return ans;
        }
    } mf;
    
    void run() {
        mf.init(500);
        int deg = 0;
        for (int i = 1; i <= n; i++) {
            cin >> d[i];
            deg += d[i];
        }
        int nxt = 150;
        for (int i = 1; i <= m; i++) {
            int u, v; cin >> u >> v;
            mf.add_edge(nxt, nxt + 1);
            for (int j = 0; j < d[u]; j++) {
                mf.add_edge(2 * u + j, nxt);
            }
            for (int j = 0; j < d[v]; j++) {
                mf.add_edge(nxt + 1, 2 * v + j);
            }
            nxt += 2;
        }
        int Max = mf.Match();
        if (deg == 2 * Max - 2 * m) {
            cout << "Yes" << '
    ';
        } else {
            cout << "No" << '
    ';
        }
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        while (cin >> n >> m) run();
        return 0;
    }
    

    J. Easy Integration

    oeis即可。
    也可以进行相关数学推导,会涉及到分部积分。可以直接通过分部积分,也可以三角换元,也可以直接利用相关公式。我就不一一赘述了。

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/12 12:16:05
    #include<bits/stdc++.h>
    #define MP make_pair
    #define fi first
    #define se second
    #define pb push_back
    #define sz(x) (int)(x).size()
    #define all(x) (x).begin(), (x).end()
    #define INF 0x3f3f3f3f
    using namespace std;
    typedef long long ll;
    typedef pair<int, int> pii;
    #define Local
    void err(int x) {cerr << x;}
    void err(long long x) {cerr << x;}
    void err(double x) {cerr << x;}
    void err(char x) {cerr << '"' << x << '"';}
    void err(const string &x) {cerr << '"' << x << '"';}
    void _print() {cerr << "]
    ";}
    template<typename T, typename V>
    void err(const pair<T, V> &x) {cerr << '{'; err(x.first); cerr << ','; err(x.second); cerr << '}';}
    template<typename T>
    void err(const T &x) {int f = 0; cerr << '{'; for (auto &i: x) cerr << (f++ ? "," : ""), err(i); cerr << "}";}
    template <typename T, typename... V>
    void _print(T t, V... v) {err(t); if (sizeof...(v)) cerr << ", "; _print(v...);}
    #ifdef Local
    #define dbg(x...) cerr << "[" << #x << "] = ["; _print(x)
    #else
    #define dbg(x...)
    #endif
    //head
    const int N = 2e6 + 5, MOD = 998244353;
    
    int qpow(ll a, ll b) {
        ll res = 1;
        while(b) {
            if (b & 1) res = res * a % MOD;
            a = a * a % MOD;
            b >>= 1;
        }
        return res;
    }
    
    int f[N];
    int fac[N];
    
    void init() {
        fac[0] = 1;
        for (int i = 1; i < N; i++) {
            fac[i] = 1ll * fac[i - 1] * i % MOD;
        }
    }
    
    int n;
    
    void run() {
        int ans = fac[2 * n + 1];
        int res = 1ll * fac[n] * fac[n] % MOD;
        ans = 1ll * ans * qpow(res, MOD - 2) % MOD;
        ans = qpow(ans, MOD - 2);
        cout << ans << '
    ';
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        init();
        while (cin >> n) run();
        return 0;
    }
    
  • 相关阅读:
    图像检索(image retrieval)- 11
    图像检索(image retrieval)- 10相关
    Mock.js简易教程,脱离后端独立开发,实现增删改查功能
    Azure Monitor (3) 对虚拟机磁盘设置自定义监控
    Azure Monitor (1) 概述
    Azure SQL Managed Instance (2) 备份SQL MI
    Azure Virtual Network (17) Private Link演示
    Azure Virtual Network (16) Private Link
    Azure Virtual Network (15) Service Endpoint演示
    Azure Virtual Network (14) Service Endpoint服务终结点
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/13307559.html
Copyright © 2011-2022 走看看