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;
    }
    
  • 相关阅读:
    嵌入式开发之hi3519---i2c EEPROM
    can 驱动
    i2c
    嵌入式开发之hi3519---spi nor flash启动
    centos7 安装后需要做的事情
    zookeeper的可视化web界面
    MySQL各类日志文件相关变量介绍
    Outlook 如何初始化邮箱
    一个不错的运维发布系统的文章
    grafana 的面板设置
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/13307559.html
Copyright © 2011-2022 走看看