2019 Multi-University Training Contest 8

传送门

D.Acesrc and Hunting

  • 将最上面两排作为中转站;
  • 自底向下往上,之后又从上往下;
  • 第三排开始每步长度为(2)或者(sqrt{2}),上面两排步长可能为(sqrt{5})

E.Acesrc and String Theory

题意:
询问(k)循环的子串个数,(1leq kleq 20)

思路:
考虑直接枚举长度为(k)的倍数的所有子串,显然时间复杂度过高。
注意到所有子串都满足存在一个最小循环节,(k=1)的情况除外,这时特判即可。
考虑对每个串直接求最小循环节,显然也会超时。
转换思路:

  • 求出所有以(x)为循环节的串,那么考虑枚举(x),然后设立(i,i+xcdots)这些关键点。
  • 依次考虑相邻的两个关键点,通过后缀数组求出(lcp,lcs),得到覆盖当前关键点的串。
  • 那么串对答案的贡献为(len-kx+1)

注意串可能会有重复的,所以我们加个判断限制一下。
详见代码:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 5;
struct SA{                                       //sa:1...n  Rank:0...n-1
    int x[N], y[N], sa[N], c[N], height[N], Rank[N];
    int f[N][20], lg[N];
    int n;                                          //length
    void da(char *s, int m){
        n++;
        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--;
        int k = 0;
        for(int i = 0; i <= n; i++) Rank[sa[i]] = i;
        for(int i = 0; i < n; i++) {
            if(k) k--;
            int j = sa[Rank[i] - 1];
            while(s[i + k] == s[j + k]) k++;
            height[Rank[i]] = k;
        }
    }
    ll count() {
        ll ans = 0;
        for(int i = 1; i <= n; i++) ans += n - sa[i] - height[i];
        return ans;
    }
    void init() {
        for(int i = 2; i < N; i++) lg[i] = lg[i >> 1] + 1;
        for(int i = 2; i <= n; i++) f[i][0] = height[i];
        for(int j = 1; j < 20; j++)
            for(int i = 2; i + (1 << j) - 1 <= n; i++)
                f[i][j] = min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]) ;
    }
    int get_lcp(int l, int r) {
        if(Rank[l] > Rank[r]) swap(l, r);
        l = Rank[l] + 1, r = Rank[r];
        int k = lg[r - l + 1];
        return min(f[l][k], f[r - (1 << k) + 1][k]);
    }
}A, B;
int T;
int k;
char s[N], t[N];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> k;
        cin >> s;
        int L = strlen(s);
        if(k == 1) {
            cout << 1ll * L * (L + 1) / 2 << '
';
            continue;
        }
        A.n = B.n = L;
        for(int i = 0; i < L; i++) t[i] = s[L - i - 1];
        t[L] = '';
        A.da(s, 520); B.da(t, 520);
        A.init(); B.init();
        int last;
        ll ans = 0;
//        cout << B.get_lcp(0, 1) << '
';
        for(int j = 1; j < L; j++) {
            last = 0;
            for(int i = last + j; i < L; i += j) {
                int r = i + A.get_lcp(last, i) - 1;
                if(r >= i + j || r >= L) continue;
                int l = last - B.get_lcp(L - i - 1, L - last - 1) + 1;
                int len = r - l + 1;
                ans += max(0, len - k * j + 1);
                last = i;
            }
        }
        cout << ans << '
';
    }
    return 0;
}

F.Acesrc and Travel

题意:
两个人在一棵树上面博弈,每到一个结点第一个人可获得(a_i)的收益,第二个人可以得到(b_i)的收益。
第一个人确定起点过后,会一直走下去直到走不通,同一个结点不会经过两次。
问最后第一个人的分数与第二个人的分数之差为多少。

思路:
注意到只要起点确定,那么方案确定,答案也就确定了,所以最后我们要枚举所有的起点取(max)得到答案。
考虑起点固定的情况:可以利用树(dp)自底向下统计答案,维护最大、次大,最小、次小值。
之后考虑换根,我们需要知道往父亲结点方向走的最大最小值,这样才能更新当前的答案。

Code
#include <bits/stdc++.h>
#define INF 0x3f3f3f3f3f3f3f3f
#define x first
#define y second
using namespace std;
typedef long long ll;
typedef pair<ll, ll> pll;
const int N = 1e5 + 5;
int T, n;
int a[N];
int tot, head[N];
struct Edge{
    int v, next;
}e[N << 1];
void adde(int u, int v) {
    e[tot].v = v; e[tot].next = head[u]; head[u] = tot++;
}
pll f[N], g[N];
int d[N];
void dfs(int u, int fa) {
    f[u].x = f[u].y = -INF;
    g[u].x = g[u].y = INF;
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v;
        if(v == fa) continue;
        dfs(v, u); d[u]++;
        if(g[v].x + a[u] > f[u].x) {
            f[u].y = f[u].x;
            f[u].x = g[v].x + a[u];
        } else if(g[v].x + a[u] > f[u].y) {
            f[u].y = g[v].x + a[u];
        }
        if(f[v].x + a[u] < g[u].x) {
            g[u].y = g[u].x;
            g[u].x = f[v].x + a[u];
        } else if(f[v].x + a[u] < g[u].y) {
            g[u].y = f[v].x + a[u];
        }
    }
    if(!d[u]) {
        f[u].x = g[u].x = a[u];
    }
}
ll p[N], q[N];
void dfs2(int u, int fa) {
    for(int i = head[u]; i != -1; i = e[i].next) {
        int v = e[i].v; ll r;
        if(v == fa) continue;
        if(d[u] == 1) p[v] = q[u] + a[v], q[v] = p[u] + a[v];
        else {
            r = (f[u].x == g[v].x + a[u]) ? f[u].y : f[u].x;
            if(u != 1) r = max(r, q[u]); p[v] = r + a[v];
            r = (g[u].x == f[v].x + a[u]) ? g[u].y : g[u].x;
            if(u != 1) r = min(r, p[u]); q[v] = r + a[v];
        }
        dfs2(v, u);
    }
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> n;
        memset(head, -1, sizeof(head)); tot = 0;
        for(int i = 1; i <= n; i++) cin >> a[i];
        for(int i = 1; i <= n; i++) {
            int k; cin >> k;
            a[i] -= k; d[i] = 0;
        }
        for(int i = 1; i < n; i++) {
            int u, v; cin >> u >> v;
            adde(u, v); adde(v, u);
        }
        dfs(1, 0);
        p[1] = q[1] = a[1];
        dfs2(1, 0);
        ll ans = g[1].x;
        for(int i = 2; i <= n; i++) {
            if(d[i]) ans = max(ans, min(p[i], g[i].x));
            else ans = max(ans, p[i]);
        }
        cout << ans << '
';
    }
    return 0;
}

H.Andy and Maze

题意:
给出一张图,每条边有一定权值,现在要求选择包含(k)个点的简单路径,问最长路径为多少。(2leq kleq 6)

思路:
随机染色。
因为(k)范围很小,所以考虑随机给图上染上(k)种颜色。然后状压(dp)维护不同颜色的路径长度的最大值。
答案路径被染成不同颜色的概率为(frac{k!}{k^k}),那么期望(frac{k^k}{k!})次染色即可把答案路径染为不同颜色。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e4 + 5;
mt19937 rnd(time(NULL));
int T, n, m, k;
int head[N], tot;
struct Edge{
    int v, w, next;
}e[N << 1];
void adde(int u, int v, int w) {
    e[tot].v = v; e[tot].w = w; e[tot].next = head[u]; head[u] = tot++;
}
int c[N];
int dp[64][N];
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        memset(head, -1, sizeof(head)); tot = 0;
        cin >> n >> m >> k;
        for(int i = 1; i <= m; i++) {
            int u, v, w; cin >> u >> v >> w;
            adde(u, v, w); adde(v, u, w);
        }
        int tot = 200;
        int ans = 0;
        while(tot--) {
            memset(dp, -1, sizeof(dp));
            for(int i = 1; i <= n; i++) {
                c[i] = rnd() % k;
                dp[1 << c[i]][i] = 0;
            }
            for(int S = 1; S < 1 << k; S++) {
                 for(int i = 1; i <= n; i++) {
                    if(S >> c[i] & 1) {
                        for(int p = head[i]; p != -1; p = e[p].next) {
                            int j = e[p].v;
                            if(dp[S ^ (1 << c[i])][j] >= 0)
                            dp[S][i] = max(dp[S][i], dp[S ^ (1 << c[i])][j] + e[p].w);
                        }
                    }
                }
            }
            int e = (1 << k) - 1;
            for(int i = 1; i <= n; i++) ans = max(ans, dp[e][i]);
        }
        if(ans == 0) cout << "impossible" << '
';
        else cout << ans << '
';
    }
    return 0;
}

I.Calabash and Landlord

题意:
在二维平面上给出两个矩形,问平面存在多少区域。

思路:
考虑离散化点,那么平面区域即为(5*5)大小。然后暴力(dfs)求连通块即可。
小trick:左闭右开区间的形式可以降低代码复杂度。

Code
#include <bits/stdc++.h>
#define y1 heyuhhh
using namespace std;
typedef long long ll;
int T;
int x[2][3], y[2][3];
int X[6], Y[6];
int mp[6][6];
bool vis[6][6];
int dx[4] = {-1, 1, 0, 0}, dy[4] = {0, 0, -1, 1};
bool ok(int x, int y, int z) {
    return x >= 0 && y >= 0 && x <= 5 && y <= 5 && !vis[x][y] && mp[x][y] == z;
}
void dfs(int x, int y, int z){
    vis[x][y] = 1;
    for(int i = 0; i < 4; i++) {
        int curx = x + dx[i], cury = y + dy[i];
        if(ok(curx, cury, z)) dfs(curx, cury, z);
    }
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        memset(vis, 0, sizeof(vis));
        memset(mp, 0, sizeof(mp));
        X[0] = Y[0] = 0;
        for(int i = 0; i < 2; i++) {
            cin >> x[i][0] >> y[i][0] >> x[i][1] >> y[i][1];
            X[++X[0]] = x[i][0]; X[++X[0]] = x[i][1];
            Y[++Y[0]] = y[i][0]; Y[++Y[0]] = y[i][1];
        }
        sort(X + 1, X + X[0] + 1); sort(Y + 1, Y + Y[0] + 1);
        X[0] = unique(X + 1, X + X[0] + 1) - X - 1;
        Y[0] = unique(Y + 1, Y + Y[0] + 1) - Y - 1;
        for(int i = 0; i < 2; i++) {
            x[i][0] = lower_bound(X + 1, X + X[0] + 1, x[i][0]) - X;
            x[i][1] = lower_bound(X + 1, X + X[0] + 1, x[i][1]) - X;
            y[i][0] = lower_bound(Y + 1, Y + Y[0] + 1, y[i][0]) - Y;
            y[i][1] = lower_bound(Y + 1, Y + Y[0] + 1, y[i][1]) - Y;
        }
        for(int k = 0; k < 2; k++)
            for(int i = x[k][0]; i < x[k][1]; i++)
                for(int j = y[k][0]; j < y[k][1]; j++)
                    mp[i][j] |= (k + 1);
        int res = 0;
        for(int i = 0; i <= 5; i++) {
            for(int j = 0; j <= 5; j++) {
                if(!vis[i][j]) {
                    dfs(i, j, mp[i][j]);
                    res++;
                }
            }
        }
        cout << res << '
';
    }
    return 0;
}

J.Quailty and CCPC

签到。

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int MAXN = 1e5 + 5, INF = 0x3f3f3f3f, MOD = 1e9 + 7;
const ll inf = 1000000000000000010LL, INFL = 0x3f3f3f3f3f3f3f3f;
#define lson o<<1,l,m
#define rson o<<1|1,m+1,r
#define mid l + ((r-l)>>1)
#define random(a,b) ((a)+rand()%((b)-(a)+1))
int t, n, d;
typedef double db;
const db eps = 1e-9;
struct node {
    char s[11];
    int p, t;
    bool operator <(const node&rhs)const {
        if (p == rhs.p)return t < rhs.t;
        return p > rhs.p;
    }
}a[MAXN];
inline int sign(db x) {
    if (fabs(x) < eps)return 0;
    return x < -eps ? -1 : 1;
}
inline int cmp(db a, db b) {
    return sign(a - b);
}
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> t;
    while (t--) {
        cin >> n >> d;
        for (int i = 1; i <= n; i++) {
            cin >> a[i].s >> a[i].p >> a[i].t;
        }
        sort(a + 1, a + 1 + n);
        db rate = n * d / 10.0;
        if (cmp(rate - (int)rate, 0.5) == 0) {
            cout << a[(int)rate + 1].s << '
';
        }
        else {
            cout << "Quailty is very great
";
        }
    }
    return 0;
}

K.Roundgod and Milk Tea

题意:
(n)个班级,每个班级有(a_i)个学生,同时制作了(b_i)份奶茶。现在问最多喝到奶茶的学生为多少,注意每个学生都不能喝他们自己班级制作的牛奶。

思路:
其实就是求最大匹配。
考虑(hall)定理的推论:

二分图的最大匹配(|M|=|U|-max_{Sin U}{|S|-|N(S)|}),其中(N(S))表示与(S)相邻的点。

然后就可以愉快地分类讨论了:

  • 最后答案如果只有一个班的学生喝了牛奶,那么只有可能是班上所有同学都喝了牛奶。因为增加学生不会改变(|N(S)|)
  • 最后答案如果有大于一个班的学生喝了牛奶,显然(N(S)=V),同时(S=U)才为最大匹配;答案即为(|V|).
  • 剩下的情况显然(max_{Sin U}{|S|-|N(S)|}=0),答案即为(|U|)

代码如下:

Code
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 6;
int T, n;
int a[N], b[N];
ll suma, sumb;
int main() {
    ios::sync_with_stdio(false); cin.tie(0);
    cin >> T;
    while(T--) {
        cin >> n;
        suma = sumb = 0;
        for(int i = 1; i <= n; i++) {
            cin >> a[i] >> b[i];
            suma += a[i]; sumb += b[i];
        }
        ll ans = min(suma, sumb);
        for(int i = 1; i <= n; i++) ans = min(ans, suma - a[i] + sumb - b[i]);
        cout << ans << '
';
    }
    return 0;
}
原文地址:https://www.cnblogs.com/heyuhhh/p/11379141.html