zoukankan      html  css  js  c++  java
  • 20200716模拟赛2题解

    A. Layout

    题目描述

    • 和人类一样,奶牛们在打饭的时候喜欢和朋友站得很近。
    • 约翰的编号为(1)(n)(nleft ( 2leq nleq 1000 ight ))只奶牛正打算排队打饭。现在请你来安排她们,让她们在数轴上排好队。奶牛的弹性很好,同一个坐标可以站无限只奶牛,排队的顺序必须和她们编号的顺序一致。有(M)对奶牛互相爱慕,她们之间的距离不能超过一定的值,有(K)对奶牛互相敌视,她们的距离不能小于一定的值。
    • 那么,首尾奶牛的最大距离是多少呢?

    输入格式

    • 第一行输入(n,M,K,left ( 0< M,Kleq 5000 ight ))
    • 接下来(M)行每行三个整数(x,y,z),表示编号为(x)(y)的两头奶牛之间的距离最大不超过(z)
    • 再接下来(K)行每行三个整数(a,b,c),表示编号为(a)(b)的两头奶牛之间的距离最少为(c)

    输出格式

    • 如果没有合理方案,输出(-1),如果首尾两头牛的距离可以无限大,输出(-2),否则输出一个整数表示首尾奶牛的最大距离。

    样例输入

    4 2 1
    1 3 10
    2 4 20
    2 3 3
    

    样例输出

    27
    

    数据范围与提示

    • 四只牛分别在(0,7,10,27)

    Solve

    • 一道裸的差分约束,没建立超级源点,丢了10分。
    • 建立超级源点是考虑到图的一部分不联通,有可能是-1判断成-2

    Code

    #include <queue>
    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N = 1e3+5, M = 12005;
    struct Side {
        int t, d, next;
    }e[M];
    int head[N], tot;
    void Add(int x, int y, int z) {
        e[++tot] = (Side) {y, z, head[x]};
        head[x] = tot;
    }
    int n, m1, m2, d[N], cnt[N];
    bool v[N];
    deque<int> q;
    int Spfa(int u) {
        memset(d, 0x3f, sizeof(d));
        memset(v, 0, sizeof(v));
        memset(cnt, 0, sizeof(cnt));
        while (!q.empty()) q.pop_front();
        d[u] = 0;
        q.push_back(u); v[u] = 1;
        cnt[u] = 1;
        while (!q.empty()) {
            int x = q.front();
            q.pop_front(); v[x] = 0;
            for (int i = head[x]; i; i = e[i].next) {
                int y = e[i].t;
                if (d[y] > d[x] + e[i].d) {
                    d[y] = d[x] + e[i].d;
                    if (v[y]) continue;
                    v[y] = 1;
                    if (++cnt[y] > n) return -1;
                    if (!q.empty() && d[y] <= d[q.front()])
                        q.push_front(y);
                    else q.push_back(y);
                }
            }
        }
        if (d[n] == 0x3f3f3f3f) return -2;
        return d[n];
    }
    int main() {
        scanf("%d%d%d", &n, &m1, &m2);
        for (int i = 1; i < n; ++i)
            Add(i + 1, i, 0);
        while (m1--) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            if (x > y) swap(x, y);
            Add(x, y, z);
        }
        while (m2--) {
            int x, y, z;
            scanf("%d%d%d", &x, &y, &z);
            if (x > y) swap(x, y);
            Add(y, x, -z);
        }
        for (int i = 1; i <= n; ++i)
            Add(0, i, 0);
        if (Spfa(0) == -1) puts("-1");
        else printf("%d
    ", Spfa(1));
        return 0;
    }
    

    B. 游戏

    题目描述

    • Mirko和Slavko爱玩弹球戏。在一个令人激动的星期五,Mirko和Slavko玩了一把弹球游戏。Mirko构建一个有向图,所有顶点最多有1条出边。弹球从1个顶点出发可以沿着一条边移动到它的邻接点,只要它存在,而且它会继续移动到后者的邻接点去,直到最后到达一个找不到出边的顶点才停下来。如果不存在这样的点,弹球可能无限运动下去。

    • 为了确信Slavko理解游戏的规则,Mirko将发起一系列询问,询问的类型如下:

      • 1 X:除非弹球陷入循环,弹球从X出发,最终将在哪个点停下来。
      • 2 X:删除X的出边(保证该边总是存在)
    • 注意:询问是按顺序执行的。

    输入格式

    • 第一行包含一个正整数N(left ( 1leq Nleq 3 imes 10^5 ight )),表示图的定点数。
    • 第二行包含由空格隔开N个正整数,第i个数表示从i顶点可以通过出边到达的定点编号。0表示该点没有出边。
    • 接下来的一行包含1个整数Q(left ( 1leq Qleq 3 imes 10^5 ight )),表示询问的次数。格式如上所示。

    输出格式

    • 对于第1类询问,输出弹球停止时所在顶点编号,每行1个,按照查询的顺序输出。如果弹球无法停止,则输出CIKLUS.

    样例输入

    3
    2 3 1
    7
    1 1
    1 2
    2 1
    1 2
    1 1
    2 2
    1 2
    

    样例输出

    CIKLUS
    CIKLUS
    1
    1
    2
    

    Solve

    • 用并查集解决,删边不好处理,可以倒过来进行加边。

    Code

    #include <cstdio>
    using namespace std;
    const int N = 3e5+5;
    int n, m, t[N], f[N], od[N], x[N], ans[N], v[N], cnt;
    bool c[N];
    int found(int x) {
        if (v[x] == cnt) return c[x] = 1, f[x] = x;
        v[x] = cnt;
        return x == f[x] ? x : (f[x] = found(f[x]));
    }
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i) {
            scanf("%d", &t[i]);
            f[i] = t[i] ? t[i] : i;
        }
        scanf("%d", &m);
        for (int i = 1; i <= m; ++i) {
            scanf("%d%d", &od[i], &x[i]);
            if (od[i] == 2) f[x[i]] = x[i];
        }
        for (int i = m; i; --i)
            if (od[i] == 1) {
                ++cnt;
                ans[i] = found(x[i]);
                if (c[ans[i]]) ans[i] = -1;
            }
            else f[x[i]] = t[x[i]];
        for (int i = 1; i <= m; ++i)
            if (ans[i] == -1) puts("CIKLUS");
            else if (ans[i]) printf("%d
    ", ans[i]);
        return 0;
    }
    

    C. 数字

    题目描述

    • 一个数字被称为好数字当他满足下列条件:
      1. 它有(2 imes n)个数位,n是正整数(允许有前导0)。
      2. 构成它的每个数字都在给定的数字集合S中。
      3. 它前n位之和与后n位之和相等或者它奇数位之和与偶数位之和相等。
    • 例如对于(n= 2,S= left { 1,2 ight }),合法的好数字有1111,1122,1212,1221,2112,2211,2222这样8种。
    • 已知n,求合法的好数字的个数(mod 999983)

    输入格式

    • 第一行一个数n。
    • 接下来一个长度不超过10的字符串,表示给定的数字集合(不存在重复的数字)。

    输出格式

    • 一行,一个整数表示合法的好数字的个数(mod 999983)

    样例输入

    2
    0987654321
    

    样例输出

    1240
    

    数据范围与提示

    • 对于20%的数据,(nleq 7)
    • 对于100%的数据,(nleq 1000,left | S ight |leq 10)

    Solve

    • 先DP处理出长度为i且所有数字和为j的方案数
      (dp[i][j]=sum_{j=0}^{i*Max\_a_i}sum_{k=1}^{len(s) } dp[i-1][j-a[k]])
    • 用分步乘法原理可得
      (ans=2 imes sum_{i=0}^{n*Max\_a_n} (dp[n][i])^2)
    • 这样会有重复,根据容斥原理可得
      (ans-=sum_{i=0}^{frac {n+1}{2}*Max\_a_n}(dp[frac{n+1}{2}][i])^2-sum_{j=0}^{frac{n}{2}*Max\_a_n}(dp[frac{n}{2}][j])^2)
    • 具体为什么是减这个数,可以看一下这一段

    Code

    #include <cstdio>
    #include <cstring>
    using namespace std;
    const int N = 1005, M = 999983;
    char c[15];
    int n, a[15];
    long long f[N][N*9], ans, s1, s2;
    long long sum(int n) {
        long long s = 0;
        for (int j = 0; j <= n * 9; ++j)
            s += f[n][j] * f[n][j] % M, s %= M;
        return s;
    }
    int main() {
        scanf("%d%s", &n, c + 1);
        a[0] = strlen(c+1);
        for (int i = 1; i <= a[0]; ++i)
            a[i] = c[i] - '0';
        f[0][0] = 1;
        for (int i = 1; i <= n; ++i)
            for (int j = 0; j <= i * 9; ++j)
                for (int k = 1; k <= a[0]; ++k)
                    if (j >= a[k]) 
                        f[i][j] += f[i-1][j-a[k]], f[i][j] %= M;
        ans = sum(n);
        ans = (ans + ans) % M;
        s1 = sum(n+1 >> 1);
        s2 = sum(n >> 1);
        ans -= s1 * s2 % M;
        ans = (ans + M) % M;
        printf("%lld", ans);
        return 0;
    }
    

    D. 水站

    题目描述

    • 已知有一个n层的水站:
      • (W_i)表示未操作之前第i层的已有水量;
      • (L_i)表示第i个水站能够维持或者储存的水的重量;
      • (P_i)表示在第i层进行减压放水操作所需的费用.
    • 被压减放水层所储存的所有水都将流向下一层。如果第i层的水量比(L_i)大,则这一层也会(自动)减压(不需要任何费用)。
    • 现在想要使最后一层减压(第n级),求最少的花费。这个任务现在交给了你。
      输入格式
    • 每个输入的第一行包含一个自然数n(left ( 1leq nleq 1.5 imes 10^5 ight ))
    • 接下来n行每行包含3个数(W_i,L_i,P_ileft ( 0leq W_i,L_i,P_ileq 1.5 imes 10^5 ight ))
      输出格式
    • 第一行输出所需的最小费用
    • 第二行若干个整数,从小到大输出必须减压的层的编号。

    样例输入

    3
    1000 1000 1
    0 1000 2
    2 10 100
    

    样例输出

    3
    1 2
    

    数据范围与提示

    • 给第一层和第二层减压
    • (30%:nleq 5000)
    • (100%:nleq 1.5 imes 10^5)

    Solve

    • 我们首先要知道最优解一定是从一个点开始到n,中途没有断掉,根据这可以写出(O(n^2))的暴力,原题就可以过了。
    • 当数据加强到1e5的时候就得考虑别的方法,可以考虑二分,对于每层来说,从上面找到一个最高的一层,使得总水量不会让这层溢出,这样这层就会对这些的价值产生影响,在用差分数组进行优化,时间复杂度为(O(n log n))

    Code

    #include <cstdio>
    #include <vector>
    using namespace std;
    const int N = 15e4+5;
    int n, w[N], m[N], p[N], s[N], c[N], ans = 1<<30, h;
    int main() {
        scanf("%d", &n);
        for (int i = 1; i <= n; ++i)
            scanf("%d%d%d", &w[i], &m[i], &p[i]),
            s[i] = s[i-1] + w[i];
        for (int i = 1; i <= n; ++i) {
            int l = 1, r = i + 1;
            while (l < r) {
                int mid = l+r >> 1;
                if (s[i] - s[mid-1] <= m[i]) r = mid;
                else l = mid + 1;
            }
            c[r] += p[i];
            c[i+1] -= p[i];
        }
        for (int i = 1; i <= n; ++i) {
            c[i] += c[i-1];
            if (ans > c[i]) ans = c[i], h = i;
        }
        printf("%d
    ", ans);
        for (int i = h; i <= n; i++)
            if (s[i] - s[h-1] <= m[i])
                printf("%d ", i);
        return 0;
    }
    
  • 相关阅读:
    hdu 1823 Luck and Love 二维线段树
    UVA 12299 RMQ with Shifts 线段树
    HDU 4578 Transformation 线段树
    FZU 2105 Digits Count 线段树
    UVA 1513 Movie collection 树状数组
    UVA 1292 Strategic game 树形DP
    【ACM】hdu_zs2_1003_Problem C_201308031012
    qsort快速排序
    【ACM】nyoj_7_街区最短路径问题_201308051737
    【ACM】nyoj_540_奇怪的排序_201308050951
  • 原文地址:https://www.cnblogs.com/shawk/p/13324635.html
Copyright © 2011-2022 走看看