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;
}
原文地址:https://www.cnblogs.com/Z8875/p/13324635.html