zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    [JSP] c:forEach 如何输出序号
    ckeditor_3.6.6.2+CKFinder2.0.2配置
    招聘面试技巧
    eclipse注释模板
    javadoc时候乱码-编码 GBK 的不可映射字符
    Java生成和操作Excel文件
    JOptionPane的使用
    2015内蒙古银行招聘网申注意事项
    Spring Security笔记:使用数据库进行用户认证(form login using database)
    中南大学oj:1336: Interesting Calculator(广搜经典题目)
  • 原文地址:https://www.cnblogs.com/heyuhhh/p/11379141.html
Copyright © 2011-2022 走看看