2023牛客暑假多校10 题解 FKLM | JorbanS

文章目录

  • F-IUPC
  • [K-First Last](https://ac.nowcoder.com/acm/contest/57364/K)
  • [L-Grayscale Confusion](https://ac.nowcoder.com/acm/contest/57364/L)
  • [M-Fair Equation](https://ac.nowcoder.com/acm/contest/57364/M)

F-IUPC

题意 n n n 题的比赛,时长为 t t t,每个题有最早过题时间的约束,每分钟内最多提交一题,任意 k k k 分钟内只能提交 m m m 题,问比赛 A K AK AK 的方案数, 1 ≤ t ≤ 300 , 1 ≤ n ≤ 13 , 1 ≤ m ≤ k ≤ 10 1\le t\le300,1\le n\le13,1\le m\le k\le10 1t300,1n13,1mk10

状态表示 f [ i ] [ j ] [ s ] f[i][j][s] f[i][j][s] 表示前 i i i 分钟已经 A C AC AC j j j 题,最后 k k k 分钟过题状态为 s s s 的方案数, s s s 状态中第 i i i 分钟的过题状态位第 k k k 位,最低位为第 i − k + 1 i-k+1 ik+1 分钟的过题状态

  • i i i 时刻不过题,从 i − 1 i-1 i1 时刻直接转移即可,这里用 a d d add add 是因为有两种状态均可转移过来
  • i i i 时刻过题,枚举每种状态 s s s,若 s s s A C AC AC 题的数量大于 m m m 则一定不满足直接 c o n t i n u e continue continue。可过的新题的个数为 s u m [ i ] − j sum[i]-j sum[i]j,即此前可交但没交的题目的数量
void add(int &x, int y) {
    x += y;
    if (x >= mod) x -= mod;
}

int main() {
    int n, t, k, m; cin >> n >> t >> k >> m;
    for (int i = 0; i < n; i ++) {
        int x; cin >> x;
        num[x] ++;
    }
    for (int i = 1; i <= t; i ++) sum[i] = sum[i - 1] + num[i];
    f[0][0][0] = 1;
    for (int i = 1; i <= t; i ++)
        for (int s = 0; s < 1 << k; s ++) {
            if (__builtin_popcount(s) > m) continue;
            for (int j = 0; j <= sum[i]; j ++) {
                add(f[i][j][s >> 1], f[i - 1][j][s]);
                if (__builtin_popcount(s >> 1) < m)
                    add(f[i][j + 1][(s >> 1) | (1 << (k - 1))], 1ll * f[i - 1][j][s] * (sum[i] - j) % mod);
            }
        }
    int res = 0;
    for (int i = 0; i < 1 << k; i ++) add(res, f[t][n][i]);
    cout << res << endl;
    return 0;
}

K-First Last

题意 一共 n n n 个人进行 m m m 场比赛,问每场比赛是第一名或者最后一名的概率

double n, m; cin >> n >> m;
printf("%.9lf\n", n == 1 ? 1. : pow(2. / n, m));

L-Grayscale Confusion

题意 序列 { v } i = 1 n \{v\}_{i=1}^n {v}i=1n 具有属性 ( a , b , c ) (a,b,c) (a,b,c) 满足 a , b , c ∈ [ 0 , 255 ] a,b,c\in[0,255] a,b,c[0,255],只有当 i i i j j j 的所有属性都较大时,则称 i i i j j j 有严格偏序关系 j ≺ i j\prec i ji,它们映射的值满足 j < i jj<i,将 n n n 个数映射到 0 ∼ 255 0\sim255 0255 之间的数,且 v 0 v_0 v0 v 1 v_1 v1 映射得到的值相等,若不能成立输出 − 1 -1 1

题解 首先判断 v 0 v_0 v0 v 1 v_1 v1 有无严格偏序关系,判断是否能够映射

法一:不断缩小范围,利用与 v [ 0 ] v[0] v[0] v [ 1 ] v[1] v[1] 具有严格偏序关系的 v [ i ] v[i] v[i] [ l , r ] [l,r] [l,r] [ 0 , 255 ] [0,255] [0,255] 缩小,因为需要具备严格偏序关系,则最多只能具有 v 1 ≺ v 2 ≺ . . . ≺ v k v_1\prec v_2\prec ...\prec v_k v1v2...vk k , k ≤ 256 k,k\le256 k,k256 个连续严格偏序关系,则映射范围是足够的,无需特判范围不够的情况

struct Color {
    int a, b, c;
    bool operator< (const Color &x) const {
        return a < x.a && b < x.b && c < x.c;
    }
};

int max(Color x) { return max(x.a, max(x.b, x.c)); }
int min(Color x) { return min(x.a, min(x.b, x.c)); }

int main() {
    int n; cin >> n;
    vector<Color> v(n);
    vector<int> ans(n);
    for (auto &[a, b, c] : v) cin >> a >> b >> c;
    if (v[0] < v[1] || v[1] < v[0]) {
        cout << -1 << endl;
        return 0;
    }
    int l = 0, r = 255;
    for (int i = 2; i < n; i ++) {
        if (v[0] < v[i] || v[1] < v[i])
            r = min(r, ans[i] = max(v[i]));
        else
            l = max(l, ans[i] = min(v[i]));
    }
    ans[0] = ans[1] = (l + r) >> 1;
    for (auto i : ans) cout << i << endl;
    return 0;
}

法二拓扑排序

拓扑排序

只适用于 D A G , 有向无环图 ( D i r e c t e d   A c y c l i c   G r a p h ) DAG,有向无环图(Directed~Acyclic~Graph) DAG,有向无环图(Directed Acyclic Graph)

用队列维护,不断将所有入度为 0 0 0 的点入队,并删除这些点

若删除的点总数不为图中的顶点数,则存在回路,无法进行拓扑排序

合并 v 0 v_0 v0 v 1 v_1 v1,对严格偏序关系的点建立边,在这个 D A G DAG DAG 上跑一次拓扑排序进行函数映射即可

struct Color {
    int a, b, c;
    bool operator< (const Color &x) const {
        return a < x.a && b < x.b && c < x.c;
    }
};

int main() {
    int n; cin >> n;
    vector<Color> v(n);
    vector<int> ans(n, 256), in(n), g[n];
    queue<int> q;
    for (auto &[a, b, c] : v) cin >> a >> b >> c;
    if (v[0] < v[1] || v[1] < v[0]) {
        cout << -1 << endl;
        return 0;
    }
    for (int i = 2; i < n; i ++) {
        for (int j = 2; j < n; j ++) {
            if (v[j] < v[i]) {
                in[j] ++;
                g[i].push_back(j);
            }
        }
        if (v[1] < v[i] || v[0] < v[i]) {
            in[1] ++;
            g[i].push_back(1);
        }
        if (v[i] < v[0] || v[i] < v[1]) {
            in[i] ++;
            g[1].push_back(i);
        }
    }
    for (int i = 1; i < n; i ++)
        if (!in[i]) {
            q.push(i);
            ans[i] = 255;
        }
    while (!q.empty()) {
        int t = q.front();
        q.pop();
        for (auto i : g[t]) {
            in[i] --;
            if (!in[i]) q.push(i);
            ans[i] = min(ans[i], ans[t] - 1);
        }
    }
    ans[0] = ans[1];
    for (auto i : ans)
        if (i == 256) {
            cout << -1 << endl;
            return 0;
        }
    for (auto i : ans) cout << i << endl;
    return 0;
}

M-Fair Equation

题意 有式子 a   +   b   =   c a~+~b~=~c a + b = c,在其中一个数字的每一位的前后皆可,插入一个数字使得等号成立,若原始也成立也可,输出 Y e s Yes Yes 和修改后的式子,否则输出 N o No No

题解 原始也成立的情况涵盖在将 0 0 0 插入某个数字的最前端,因此只要分三类

bool check(int a, int b) {
    for (int i = 0; i <= to_string(a).size(); i ++) {
        int base = pow(10, i);
        int aa = a % base + a / base * base * 10;
        for (int j = 0; j < 10; j ++)
            if (aa + base * j == b) return true;
    }
    return false;
}

void out(int a, int b, int c) {
    cout << "Yes" << endl << a << " + " << b << " = " << c << endl;
}

void solve() {
    int a, b, c; char ch; cin >> a >> ch >> b >> ch >> c;
    if (check(a, c - b)) out(c - b, b, c);
    else if (check(b, c - a)) out(a, c - a, c);
    else if (check(c, a + b)) out(a, b, a + b);
    else cout << "No" << endl;
}

你可能感兴趣的:(OI,题解,算法,c++)