2020 Multi-University Training Contest 2

2020 Multi-University Training Contest 2

HUD 6763 The Oculus

题意:

给你一张图, 你每次选一个联通块,每个点有个权值, 你可以设所有权值减一, 当有权值为0是, 与这个点连接的边将断开, 问最少操作多少次能使所有点的权值为0.

题解:

如果暴力, 那么每次肯定是一快减去最小值,然后分成n个块继续每个块减去去小值, 然后又分成n个块……

时间复杂度 > (n ^ 2)那肯定是过不了的。

如果你能再脑海里把这个操作倒着想一边, 你应该就能明白怎么写了。

如果你想不通就再想一边。

太困了我不想解释了, 一句两句也说不清。 自己想吧。

代码:

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

const int N = 1e5 + 7;

vector<int> g[N];

int n, m, t, a[N], fa[N];

int find(int x) {
    if (x != fa[x]) {
        return fa[x] = find(fa[x]);
    } 
    return x;
}

vector<pair<int, int> >v;
int vis[N];

int main() {
    
    scanf("%d", &t);
    while (t--) {
        scanf("%d %d", &n, &m);
        v.clear();
        for (int i = 0; i <= n; i++) {
            fa[i] = i;
            g[i].clear();
            vis[i] = 0;
        }
        for (int i = 1; i <= n; i++) {
            scanf("%d", &a[i]);
            v.push_back({a[i], i});
        }
        sort(v.begin(), v.end(), [](pair<int, int> x, pair<int, int> y) {
            return x.first > y.first;
        });

        for (int i = 1; i <= m; i++) {
            int u, v;
            scanf("%d %d", &u, &v);
            g[u].push_back(v);
            g[v].push_back(u);
        }
        ll ans = 0;
        ll count = 0;
        v.push_back({0, 0});
        for (int i = 0; i < v.size() - 1; i++) {
            int pos = v[i].second;
            int cost = v[i].first;
            int fx = find(pos);
            vis[pos] = 1;
            count++;
            for (int to: g[pos]) {
                if (!vis[to]) continue;
                int fy = find(to);
                if (fx != fy) {
                    fa[fy] = fx;
                    count--;
                }
            }
           ans += (count) * 1ll*(cost - v[i + 1].first);

            
        }
        printf("%lld
", ans);

        

        
    }
}

hdu 6768 The Oculus

题意: 给一个 斐波拉契数列, 个一个01串 a, b第i有1表示 a串的值 加上fn[i] (fn为斐波拉契数列)。

a * b = c + fn[i] 让你求i

题解:

fn[i] = a * b - c;

由于 a * b溢出 所有取模 找个模数,使得 fn[i] % mod != fn[j] % mod

代码:

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

const int N = 2e6 + 7;

ll fn[N];

int t, n, m;


int main() {
    fn[1] = 1, fn[2] = 2;
    ll mod = 787654321;

    for (int i = 3; i < N; i++) {
        fn[i] = (fn[i - 1]  + fn[i - 2] ) % mod;
    }
    scanf("%d", &t);
    while (t--) {
        scanf("%d", &n);
        ll ans = 0;
        for (int i = 1; i <= n; i++) {
            int x; scanf("%d", &x);
            if (x == 1) {
                ans = (ans  + fn[i] ) % mod;
            } 
        }
        ll ans1 = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int x; scanf("%d", &x);
            if (x == 1) {
                ans1 = (ans1  + fn[i] ) % mod;
            }
        }
        ll ans2 = 0;
        scanf("%d", &n);
        for (int i = 1; i <= n; i++) {
            int x; scanf("%d", &x);
            if (x == 1) {
                ans2 = (ans2 + fn[i] ) % mod;
            }
        }
        ans = ((ans % mod * ans1 % mod)%mod - ans2  + mod) % mod;
        for (int i = 1; i < N; i++) {
            if (fn[i] == ans) {
                printf("%d
", i);
                break;
            }
        }
        
    }
}

[hdu - 6772] (http://acm.hdu.edu.cn/showproblem.php?pid=6772)

题解:

有n个物品, 有t个种类, 每个种类取一个问贡献最大是多少。

题解:

直接dfs 暴力

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 100;

int t, n, k;

struct node {
    ll a, b, c, d;
};

vector<node>p[100];
vector<int>v;

ll ans;

void dfs(int pos, ll a, ll b, ll c, ll d) {
    
    if (pos >= v.size()) {
       ll cnt = (a + 100) * (b + 100) * (c + 100) * (d + 100);
       ans = max(ans, cnt);
       return; 
    }
    
    for (int i = 0; i < p[v[pos]].size(); i++) {
        dfs(pos + 1, a + p[v[pos]][i].a, b + p[v[pos]][i].b, c + p[v[pos]][i].c, d + p[v[pos]][i].d);
    }

}

int main() {
    scanf("%d", &t);
    while (t--) {
        ans = 0;
        v.clear();
        scanf("%d %d", &n, &k);
        for (int i = 0; i <= k; i++) {
            p[i].clear();
        }
        for (int i = 1; i <= n; i++) {
            ll t, a, b, c, d;
            scanf("%lld %lld %lld %lld %lld", &t, &a, &b, &c, &d);
            p[t].push_back({a, b, c, d});
            v.push_back(t);
        }
        sort(v.begin(), v.end());
        v.erase(unique(v.begin(), v.end()), v.end());
        dfs(0, 0, 0, 0, 0);
        printf("%lld
", ans);

    }
}

hdu 6774 String Distance

题意:

给你一个字符串 a, b问, a中子串 l到r,每次操作可以删掉一个字符或者增加一个字符, 问最少操作多少次可以使 l 到r的子串等于 a.

题解:

我们发现操作 增加是没用用, 因为我们要再, a串中增加一个字符, 和再b串中删除一个字符效果是一样的。

所以最后匹配的就是两个字符串的 最长上升子序列, 答案就是 (r - l + 1 + m - 2 * lsc)

因b串的长度只有 20, 而询问有1e5, 如果直接求lcs复杂度肯定过不了。

所以用 一个序列自动机。

(nex[i][j])表示 字符 a中 第 i 个位置, 后面出现第一个字符 j的位置。

$dp[i][j] $ 表示 字符串 b 位于 第 i 个位置, 得到长度为j lsc 的 最后一个字符的最前面的位置。

(nex[i][j])很好求。

关键是 (dp[i][j])

初始化 (dp[i][j]) 无穷大, (dp[0][0] = l - 1, dp[i][0] = l - 1)

(dp[i][j] = min(dp[i][j], dp[i - 1][j]))

(dp[i][j] = min(dp[i][j], nex[dp[i - 1][j - 1]][t[i]]) and nex[dp[i - 1][j - 1][t[i]]] <= r)

代码:

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

const int N = 1e5 + 7;

int t, n, m, nxt[N][200], vis[200], dp[300][300];

char a[N], b[30];

int main() {

    scanf("%d", &t);
    while (t--) {
        scanf("%s %s", (a + 1),  (b + 1));
        int la = strlen(a + 1), lb = strlen(b + 1);
        for (int i = 'a'; i <= 'z'; i++) vis[i] = la + 10;
        for (int i = la; i; i--) {
           
            for (int j = 'a'; j <= 'z'; j++) {
                nxt[i][j] = vis[j];
            } 
            vis[a[i]] = i;
        }
        for (int j = 'a'; j <= 'z'; j++) nxt[0][j] = vis[j];
        int q; scanf("%d", &q);
        while (q--) {
            int l, r; scanf("%d %d", &l, &r);
            for (int i = 0; i <= lb; i++) {
                for (int j = 0; j <= lb; j++) {
                    dp[i][j] = 1e8;
                }
            }
            dp[0][0] = l - 1;
            for (int i = 1; i <= lb; i++) {
                dp[i][0] = l - 1;
                for (int j = 1; j <= i; j++) {
                    dp[i][j] = min(dp[i][j], dp[i - 1][j]);
                    if (dp[i - 1][j -  1] <= r &&nxt[dp[i - 1][j - 1]][b[i]] <= r) {
                        dp[i][j] = min(dp[i][j], nxt[dp[i - 1][j - 1]][b[i]]);
                    }
                }
            }
            for (int i = lb; i >= 0; i--) {
               if (dp[lb][i] <= r) {
                   printf("%d
", (r - l + 1 + lb) - 2 * i);
                   break;
               }
            }
        }

    }
}
原文地址:https://www.cnblogs.com/BOZHAO/p/13369821.html