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

    Contest Info


    传送门

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

    Solutions


    A. Portal

    题意:
    给出一张(n)个点,(m)条边的无向图,现在有(k)个认为,每个任务给定(u_i,v_i),意即要先到(u_i),然后再到(v_i)
    现在可以在当前所在位置设置传送门,两个传送门之间可以瞬时传送,图上最多存在两个传送门。
    问从(1)号点出发,完成所有任务的最小路径。

    思路:
    朴素解法:(dp[i][j][p])表示当前在点(i),在做任务(j)并且(p)位置有个传送门。那么可以直接分情况进行转移。但是复杂度为(O(n^4))
    考虑同一个阶段的两个子任务是等价的,所以将他们拆分为两个任务,所以现在任务就变为了从一个点到另外一个点。这样的话我们可以减少一维“位置”的状态,因为起点和终点固定,所以可以直接通过枚举中间点进行转移。
    时间复杂度为(O(n^3))

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/25 15:29:35
    #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 = 300 + 5;
     
    int n, m, k;
    ll dis[N][N];
    ll dp[N << 1][N];
     
    void chkmin(ll& x, ll y) {
        if (x > y) x = y;
    }
     
    void run() {
        cin >> n >> m >> k;
        memset(dis, INF, sizeof(dis));
        for (int i = 1; i <= m; i++) {
            int u, v, W;
            cin >> u >> v >> W;
            if ((ll)W <= dis[u][v]) {
                dis[u][v] = dis[v][u] = W;
            }
        }
        for (int i = 1; i <= n; i++) {
            dis[i][i] = 0;
        }
        for (int k = 1; k <= n; k++) {
            for (int i = 1; i <= n; i++) {
                for (int j = 1; j <= n; j++) {
                    dis[i][j] = min(dis[i][j], dis[i][k] + dis[k][j]);
                }
            }
        }
        vector<int> vs;
        vs.push_back(1);
        for (int i = 0; i < k; i++) {
            int u, v;
            cin >> u >> v;
            vs.push_back(u);
            vs.push_back(v);
        }
        int sz = sz(vs);
        memset(dp, INF, sizeof(dp));
        ll MAX = ll(0x3f3f3f3f3f3f3f3f);
        dp[0][1] = 0;
        for (int i = 0; i < sz - 1; i++) {
            int u = vs[i], v = vs[i + 1];
            for (int p = 1; p <= n; p++) {
                if (dp[i][p] != MAX) {
                    chkmin(dp[i + 1][p], dp[i][p] + dis[u][v]);
                    chkmin(dp[i + 1][p], dp[i][p] + dis[p][v]);
                    for (int q = 1; q <= n; q++) {
                        chkmin(dp[i + 1][q], min(dp[i][p] + dis[u][q] + dis[p][v], dp[i][p] + min(dis[u][q], dis[p][q]) + dis[q][v]));
                    }
                }
            }
        }
        ll ans = MAX;
        for (int i = 1; i <= n; i++) {
            ans = min(ans, dp[sz - 1][i]);
        }
        cout << ans << '
    ';
    }
    int main() {
    #ifdef Local
        freopen("input.in", "r", stdin);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    B. Graph

    有这样一个性质,任一时刻,连接两点((u,v))他们的边权值都是相等的。
    证明:考虑现在添加一条边有多个环,那么显然这条边的权值只有一个,也就是其余所有路径上的边异或值相等,那么就容易发现任一状态这条边的权值都是相等的。
    然后就是异或最小生成树。可以直接通过字典序自底向上合并即可。每次合并就贪心找权值最小的合并。

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/25 18:53:18
    #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;
    
    int n;
    vector<pii> G[N];
    int a[N];
    
    void dfs(int u, int fa, int val) {
        a[u] = val;
        for (auto it : G[u]) {
            int v = it.fi, w = it.se;
            if (v == fa) continue;
            dfs(v, u, val ^ w);
        }
    }
    
    int ch[N * 31][2];
    int tot;
    
    void insert(int x) {
        int p = 0;
        for (int i = 29; i >= 0; i--) {
            int op = 0;
            if (x >> i & 1) {
                op = 1;
            }
            if (!ch[p][op]) {
                ch[p][op] = ++tot;
            }
            p = ch[p][op];
        }
    }
    
    ll ans;
    
    int calc(int p1, int p2, int dep) {
        int res = INF;
        if (ch[p1][0] && ch[p2][0]) {
            res = calc(ch[p1][0], ch[p2][0], dep - 1);
        }
        if (ch[p1][1] && ch[p2][1]) {
            res = min(res, calc(ch[p1][1], ch[p2][1], dep - 1));
        }
        if (res != INF) return res;
        if (ch[p1][0] && ch[p2][1]) {
            res = min(res, calc(ch[p1][0], ch[p2][1], dep - 1) + (1 << dep));
        }
        if (ch[p1][1] && ch[p2][0]) {
            res = min(res, calc(ch[p1][1], ch[p2][0], dep - 1) + (1 << dep));
        }
        if (res == INF) res = 0;
        return res;
    }
    
    void work(int p, int dep) {
        if (ch[p][0]) {
            work(ch[p][0], dep - 1);
        }
        if (ch[p][1]) {
            work(ch[p][1], dep - 1);
        }
        if (ch[p][0] && ch[p][1]) {
            ans += (1 << dep);
            ans += calc(ch[p][0], ch[p][1], dep - 1);
        }
    }
    
    void run() {
        cin >> n;
        for (int i = 1; i < n; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            ++u, ++v;
            G[u].push_back(MP(v, w));
            G[v].push_back(MP(u, w));
        }
        dfs(1, 0, 0);
        for (int i = 1; i <= n; i++) {
            insert(a[i]);
        }
        work(0, 29);
        cout << ans << '
    ';
    }
    int main() {
    #ifdef Local
        freopen("input.in", "r", stdin);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    C. Easy

    题意:
    求两个正整数序列(A,B),满足(sum_{i=1}^ka_i=n,sum_{i=1}^kb_i=m)时,(sumprod_{i=1}^{k}min(a_i,b_i))的值。
    (n,mleq 10^6)

    思路:
    愉快的推公式题,考虑二元生成函数,我们首先来求(displaystyle f(x,y)=sum_{i=1}^{infin}sum_{j=1}^{infin}min(i,j)x^iy^j)的值,那么([x^ny^m]f(x)^k)即是答案。
    这种我们肯定首先考虑将min拆开,所以有:

    [egin{aligned} f(x,y)=&sum_{i=1}^{infin}ix^iy^i+sum_{i=1}^{infin}sum_{j=i+1}^{infin}ix^iy^j+sum_{j=1}^{infin}sum_{i=j+1}^{infin}jx^iy^j\ =&sum_{i=1}^{infin}i(xy)^i[1+y+y^2+cdots+x+x^2+cdots]\ =&(frac{y}{1-y}+frac{x}{1-x}+1)cdot frac{xy}{(1-xy)^2}\ =&frac{xy}{(1-x)(1-y)(1-xy)} end{aligned} ]

    这个推出来那么后面就比较简单了,因为要求(f(x,y)^k),我们首先看(displaystylefrac{xy}{1-xy}),这个就等于(displaystyle sum_{i=1}^{infin}x^iy^i),很容易求出对其(k)次方过后对应项的系数,根据二项式定理和组合数算一下就行,剩下的不够(n,m)的直接从(frac{1}{1-x},frac{1}{1-y})里面补就行。

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/25 21:05:25
    #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 = 1e6 + 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 fac[N], inv[N];
    void init() {
        fac[0] = 1;
        for(int i = 1; i < N; i++) fac[i] = 1ll * fac[i - 1] * i % MOD;
        inv[N - 1] = qpow(fac[N - 1], MOD - 2);
        for(int i = N - 2; i >= 0; i--) inv[i] = 1ll * inv[i + 1] * (i + 1) % MOD;
    }
    
    int C(int n, int m) {
        return 1ll * fac[n] * inv[m] % MOD * inv[n - m] % MOD;
    }
    
    int n, m, k;
    
    int calc(int x) {
        return C(x + k - 1, k - 1);
    }
    
    void run() {
        cin >> n >> m >> k;
        int ans = 0;
        for (int i = 0; i + k <= min(n, m); i++) {
            ans = (ans + 1ll * C(k - 1 + i, k - 1) * calc(n - (i + k)) % MOD * calc(m - (i + k)) % MOD) % MOD;
        }
        cout << ans << '
    ';
    }
    int main() {
    #ifdef Local
        freopen("input.in", "r", stdin);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        init();
        int T; cin >> T; while(T--)
        run();
        return 0;
    }
    

    D. Drop Voicing

    操作就等价于将一个数插入在任意一个位置。
    那么直接枚举所有可能循环,求lis即可。

    Code
    #include<bits/stdc++.h>
     
    using namespace std;
     
    #define fi first
    #define se second
    #define pb push_back
    #define mp make_pair
    #define LC k<<1
    #define RC k<<1|1
     
    typedef long long LL;
    const int N=510;
    const int M=1100000;
    const LL mod=1e9+7;
     
    int n;
    int a[N],b[N], f[N];
    int ans;
    int main()
    {
        scanf("%d",&n);
        for (int i=0;i<n;i++)
            scanf("%d",&a[i]),a[i]--;
        ans=n;
        for (int i=0;i<n;i++)
        {
            int tmp=1;
            for (int j=0;j<n;j++)
                b[(i+j)%n]=a[j];
            for (int j = 0; j < n; j++) {
                f[j] = 1;
            }
            for (int j = 1; j < n; j++) {
                for (int k = 0; k < j; k++) {
                    if (b[j] > b[k])
                    f[j] = max(f[j], f[k] + 1);
                }
                tmp = max(tmp, f[j]);
            }
            ans=min(ans,n - tmp);
        }
        printf("%d
    ",ans);
        return 0;
    }
    

    E. Bogo Sort

    对所有环长求lcm即是答案,不用取模。

    Code
    n = int(input())
    a = input().strip().split(" ")
    pp = [0 for _ in range(n)]
     
    for i in range(n):
        a[i] = int(a[i]) - 1
     
    ans = 1
    pw = 1
    for i in range(n):
        pw = pw * 10
    flag = False
    for i in range(n):
        if pp[i] == 0:
            now = i
            len = 0
            mx = 0
            num = 0
            last = -1
            tmp = []
            while pp[now] == 0:
                tmp.append(now)
                pp[now] = 1
                if now > last:
                    last = now
                else:
                    last = now
                    num += 1
                if num == 1:
                    if tmp[-1] > tmp[0]:
                        flag= True
                elif num > 1:
                    flag = True
                now = a[now]
                len += 1
            if flag:
                ans = 0
                break
            ans = ans * len
    print(ans % pw)
    

    F. DPS

    签到。

    Code
    
    

    H. Interval

    题意:
    给定一个序列(A),然后会有若干组询问,每组询问一段区间([l,r]),回答区间中连续子区间中的数的交一共有多少种。强制在线。

    思路:
    有一个比较重要的性质:

    • 固定一个区间的左端点,那么不同的数一共只会有(O(logA))种。

    所以一共只会有(O(nlogA))种数值,我们把每种数值的区间扣出来,那么对于一个区间查询而言实际上就是一个普通二位偏序,可以通过主席树解决。
    问题在于如何去重,比如一个区间覆盖了([l_1,r_1],[l_2,r_2],[l_3,r_3])这三段,但实际上贡献只会产生(1)。这里实际上是个小技巧,我们只需要新增加贡献为(-1)([l_1,r_2],[l_2,r_3])即可。
    时间复杂度和空间复杂度都是(O(nlog^2n))的。
    细节见代码:

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/26 11:22:01
    #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;
    
    int n;
    int f[N][20], lg[N];
    
    void init() {
        lg[2] = 1;
        for (int i = 3; i < N; i++) {
            lg[i] = lg[i >> 1] + 1;
        }
        for (int j = 1; j < 20; j++) {
            for (int i = 1; i <= n; i++) {
                if (i + (1 << (j - 1)) <= n) {
                    f[i][j] = (f[i][j - 1] & f[i + (1 << (j - 1))][j - 1]);
                }
            }
        }
    }
    
    int query(int l, int r) {
        int k = lg[r - l + 1];
        return (f[l][k] & f[r - (1 << k) + 1][k]);
    }
    
    map<int, vector<pii>> mp;
    vector<pii> v[N];
    
    int rt[N], tot;
    int lc[N * 30 * 23], rc[N * 30 * 23], sumv[N * 30 * 23];
    
    void build(int &o, int l, int r) {
        o = ++tot;
        if(l == r) return;
        int mid = (l + r) >> 1;
        build(lc[o], l, mid); build(rc[o], mid + 1, r);
    }
    
    void insert(int& o, int last, int l, int r, int p, int v) {
        o = ++tot;
        lc[o] = lc[last], rc[o] = rc[last];
        sumv[o] = sumv[last] + v;
        if (l == r) return;
        int mid = (l + r) >> 1;
        if (p <= mid) insert(lc[o], lc[last], l, mid, p, v);
        else insert(rc[o], rc[last], mid + 1, r, p, v);
    }
    
    int query(int o, int l, int r, int L, int R) {
        if (!o) return 0;
        if (L <= l && r <= R) {
            return sumv[o];
        }
        int res = 0;
        int mid = (l + r) >> 1;
        if (L <= mid) {
            res += query(lc[o], l, mid, L, R);
        }
        if (R > mid) {
            res += query(rc[o], mid + 1, r, L, R);
        }
        return res;
    }
    
    void run() {
        cin >> n;
        for (int i = 1; i <= n; i++) {
            cin >> f[i][0];
        }
        init();
        for (int i = 1; i <= n; i++) {
            for (int j = i; j <= n; j++) {
                int l = j + 1, r = n + 1, mid;
                int k = query(i, j);
                while (l < r) {
                    mid = (l + r) >> 1;
                    if (query(i, mid) == k) l = mid + 1;
                    else r = mid;
                }
                mp[k].push_back(MP(i, j));
                // cout << k << ' ' << i << ' ' << l - 1 << '
    ';
                j = l - 1;
            }
        }
        for (auto it : mp) {
            sort(all(it.se), [&](pii A, pii B) {
                if (A.fi != B.fi) return A.fi < B.fi;
                return A.se < B.se;
            });
            vector<pii>& now = it.se;
            for (int i = 0; i < sz(now); i++) {
                v[now[i].se].push_back(MP(now[i].fi, 1));
                if (i) {
                    v[now[i].se].push_back(MP(now[i - 1].fi, -1));
                }
            }
        }
        build(rt[0], 1, n);
        for (int i = 1; i <= n; i++) {
            sort(all(v[i]));
            rt[i] = rt[i - 1];
            for (auto it : v[i]) {
                insert(rt[i], rt[i], 1, n, it.fi, it.se);
            }
        }
        int q; cin >> q;
        int lastans = 0;
        while (q--) {
            int l, r;
            cin >> l >> r;
            l = (l ^ lastans) % n + 1;
            r = (r ^ lastans) % n + 1;
            if (l > r) swap(l, r);
            int ans = query(rt[r], 1, n, l, r);
            cout << ans << '
    ';
            lastans = ans;
        }
    }
    int main() {
    #ifdef Local
        freopen("input.in", "r", stdin);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    

    I. Hard Math Problem

    答案就为(frac{2}{3})
    主要思路就是一个E周围贡献四个H,G用来匹配已经产生了贡献的H,那么相当于一个G对应零个H,所以总的比例就为(frac{2}{3})

    Code
    // Author : heyuhhh
    // Created Time : 2020/07/25 12:31:21
    #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;
    void run() {
        cout << "0.666667" << '
    ';
    }
    int main() {
    #ifdef Local
        freopen("input.in", "r", stdin);
    #endif
        ios::sync_with_stdio(false);
        cin.tie(0); cout.tie(0);
        cout << fixed << setprecision(20);
        run();
        return 0;
    }
    
  • 相关阅读:
    Android入门:Button
    Android入门:部署时的常见错误
    to be
    Android入门:单元测试
    忘记 MySQL 的 root 帐号密码该怎么办
    Eclipse开发build path中jar包部署到应用中
    报告两个bug
    本站导引
    一个用Word做报表设计的报表系统windwardreports
    智能互联网
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/13389870.html
Copyright © 2011-2022 走看看