AtCoder Beginner Contest 371

A - Jiro

输入 \(AB, BC, AC\) 的大小关系,输出中位数。

手动模拟一下。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

char c, a, b;

int main()
{
    scanf(" %c", &c);
    scanf(" %c %c", &a, &b);
    if(c == '<')
    {   
        if(a == '>' && b == '>') printf("A");
        if(a == '<' && b == '>') printf("C");
        if(a == '<' && b == '<') printf("B");
    }else
    {
        if(a == '>' && b == '>') printf("B");
        if(a == '>' && b == '<') printf("C");
        if(a == '<' && b == '<') printf("A");
    }
    return 0;
}

B - Taro

每家的第一个男孩子被命名为 \(\text{Taro}\),按照出生顺序输出每个孩子所在家庭和性别,判断他是否被命名为 \(\text{Taro}\)

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 105;

int vis[N];

int main()
{
    int n = read(), m = read();
    int a;
    char c;
    for(int i = 1; i <= m; ++i)
    {
        scanf("%d %c", &a, &c);
        if(c == 'M' && !vis[a])
        {
            vis[a] = 1;
            printf("Yes\n");
        }else printf("No\n");
    }
    return 0;
}

C - Make Isomorphic

给定两个 \(n\) 个点数的图 \(G, H\),可以用 \(A_{i, j}\) 的代价删去或加上边 \((i, j)\),问最小的花费使得图 \(G, H\) 同构。

同构的定义是,存在一个排列 \(p\),使得 \(G\) 中存在边 \((i, j)\) 当且仅当 \(H\) 中存在边 \((p_i, p_j)\)

发现点数很小,暴力枚举排列 \(p\)计算代价即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 10;
int n, mG, mH;
int visG[N][N], visH[N][N];
int val[N][N];
int ans, now;
int p[N];
int main()
{
    ans = 0x7fffffff;
    n = read();
    mG = read();
    for(int i = 1; i <= mG; ++i)
    {
        int u = read(), v = read();
        visG[u][v] = 1;
    }
    mH = read();
    for(int i = 1; i <= mH; ++i)
    {
        int u = read(), v = read();
        visH[u][v] = 1;
    }
    for(int i = 1; i <= n; ++i)
        for(int j = i + 1; j <= n; ++j)
            val[i][j] = read();
    for(int i = 1; i <= n; ++i) p[i] = i;
    do
    {
        now = 0;
        for(int i = 1; i <= n; ++i)
            for(int j = i + 1; j <= n; ++j)
            {
                int u = p[i], v = p[j];
                if(u > v) swap(u, v);
                if(visG[i][j] ^ visH[u][v]) now += val[u][v];
            }
        ans = min(ans, now);
    }while(next_permutation(p + 1, p + n + 1));
    printf("%d\n", ans);
    return 0;
}

D - 1D Country

\(n\) 个位置 \(X_i\),每个位置有 \(P_i\) 个人,每次询问位于 \([L_i, R_i]\) 位置中的人的个数。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 6e5 + 5;
int n, q, nn, m;
int X[N], L[N], R[N], a[N], b[N];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

ll sum[N << 2];

void update(int k, int l, int r, int pos, int val)
{
    if(l == r){ sum[k] += val; return ; }
    int mid = (l + r) >> 1;
    if(pos <= mid) update(ls(k), l, mid, pos, val);
    else update(rs(k), mid + 1, r, pos, val);
    sum[k] = sum[ls(k)] + sum[rs(k)];
}

ll query(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return sum[k];
    int mid = (l + r) >> 1;
    if(R <= mid) return query(ls(k), l, mid, L, R);
    if(L > mid) return query(rs(k), mid + 1, r, L, R);
    return query(ls(k), l, mid, L, R) + query(rs(k), mid + 1, r, L, R);
}

int main()
{
    n = read();
    for(int i = 1; i <= n; ++i) X[i] = read(), b[++m] = X[i];
    for(int i = 1; i <= n; ++i) a[i] = read();
    q = read();
    for(int i = 1; i <= q; ++i)
    {
        L[i] = read(), b[++m] = L[i];
        R[i] = read(), b[++m] = R[i];
    }
    sort(b + 1, b + m + 1);
    nn = unique(b + 1, b + m + 1) - (b + 1);
    for(int i = 1; i <= n; ++i) X[i] = lower_bound(b + 1, b + nn + 1, X[i]) - b;
    for(int i = 1; i <= q; ++i)
    {
        L[i] = lower_bound(b + 1, b + nn + 1, L[i]) - b;
        R[i] = lower_bound(b + 1, b + nn + 1, R[i]) - b;
    }
    for(int i = 1; i <= n; ++i) update(1, 1, nn, X[i], a[i]);
    for(int i = 1; i <= q; ++i) printf("%lld\n", query(1, 1, nn, L[i], R[i]));
    return 0;
}

E - I Hate Sigma Problems

给定序列 \(A\) ,求 \(A\) 的所有子序列的不同数值的种类数和。

考虑扫描线,每次扩展右端点,维护所有左端点的答案,记 \(last_x\) 表示值 \(x\) 上一次出现的位置,那么对于 \(r\)\(a_r\) 是区间 \([last_{a_r} + 1, r]\)中的新数,对于这些左端点加 \(1\) 即可。

线段树维护一下区间加,求全局和即可。

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 2e5 + 5;
int n, a[N], last[N];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

ll sum[N << 2], lazy[N << 2], len[N << 2];

void pushdown(int k)
{
    if(lazy[k])
    {
        sum[ls(k)] += len[ls(k)] * lazy[k];
        sum[rs(k)] += len[rs(k)] * lazy[k];
        lazy[ls(k)] += lazy[k];
        lazy[rs(k)] += lazy[k];
        lazy[k] = 0;
    }
}

void build(int k, int l, int r)
{
    len[k] = r - l + 1;
    if(l == r) return ;
    int mid = (l + r) >> 1;
    build(ls(k), l, mid), build(rs(k), mid + 1, r);
}

void update(int k, int l, int r, int L, int R, int val)
{
    if(L <= l && r <= R)
    {
        lazy[k] += val;
        sum[k] += len[k] * val;
        return ;
    }
    pushdown(k);
    int mid = (l + r) >> 1;
    if(L <= mid) update(ls(k), l, mid, L, R, val);
    if(R > mid) update(rs(k), mid + 1, r, L, R, val);
    sum[k] = sum[ls(k)] + sum[rs(k)];
}

ll query(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return sum[k];
    pushdown(k);
    int mid = (l + r) >> 1;
    if(R <= mid) return query(ls(k), l, mid, L, R);
    if(L > mid) return query(rs(k), mid + 1, r, L, R);
    return query(ls(k), l, mid, L, R) + query(rs(k), mid + 1, r, L, R);
}

int main()
{
    n = read();
    ll ans = 0;
    build(1, 1, n);
    for(int i = 1; i <= n; ++i)
    {
        a[i] = read();
        update(1, 1, n, last[a[i]] + 1, i, 1);
        last[a[i]] = i;
        ans += sum[1];
    }
    printf("%lld\n", ans);
    return 0;
}

F - Takahashi in Narrow Road

\(n\) 个人站在一排位置上,第 \(i\) 次命令第 \(T_i\) 个人到达 \(G_i\) 位置,人与人之间不会站在同一位置,也不会跨过对方,也就是让第 \(1\) 个人到达第四个人所在的位置,需要第二三四个人都向右移动一些步。问最少需要多少步能完成所有命令,命令必须按顺序完成。

发现一个人会把其他人推到某个位置,然后排成连续的一列,如果位置很少的话可以考虑区间覆盖,区间求和的一系列操作,但是 \(1e8\) 的位置很可能会都被到达过,空间不允许。

若设 \(x_i' = x_i - i\),那么排成一列的人会在同一个位置。

\(G_i' = G_i - i\),不妨设 \(x_i' < G_i'\),找到最大的 \(j\) 满足 \(i \le j\)\(x_j' \le G_i\),那么第 \(i\) 到第 \(j\) 个人就是完成此次命令需要挪步的人,总步数为 \(\sum_{l=i}^{j}(G_i'-X_l')\),同时将这 \(j-i+1\) 个人的位置更新为 \(G_i'\)

发现对于每个命令,每次最多出现一个新位置,空间允许。

先离散化,线段树维护每一段区间有多少人,所有人的位置之和,快速找到第 \(i\) 个所在位置,找到在第 \(i\) 个位置及之前的人的个数,区间清空,单点加。

线段树开4倍空间!!!

点击查看代码
#include<bits/stdc++.h>
using namespace std;

#define ll long long
#define ull unsigned long long

int read()
{
    int x = 0; bool f = false; char c = getchar();
    while(c < '0' || c > '9') f |= (c == '-'), c = getchar();
    while(c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c & 15), c = getchar();
    return f ? -x : x;
}

const int N = 6e5 + 5;
int n, Q;
int X[N], x[N], b[N], T[N], G[N], g[N];

#define ls(x) (x << 1)
#define rs(x) (x << 1 | 1)

ll lazy[N << 2], sum[N << 2], cnt[N << 2]; // 区间赋0标记

void pushup(int k)
{
    sum[k] = sum[ls(k)] + sum[rs(k)];
    cnt[k] = cnt[ls(k)] + cnt[rs(k)];
}

void pushdown(int k)
{
    if(lazy[k])
    {
        sum[ls(k)] = sum[rs(k)] = 0;
        cnt[ls(k)] = cnt[rs(k)] = 0;
        lazy[ls(k)] = lazy[rs(k)] = 1;
        lazy[k] = 0;
    }
}

void update1(int k, int l, int r, int pos, int Size)
{
    if(l == r)
    {
        sum[k] += 1ll * Size * b[pos];
        cnt[k] += Size;
        return ;
    }
    pushdown(k);
    int mid = (l + r) >> 1;
    if(pos <= mid) update1(ls(k), l, mid, pos, Size);
    else update1(rs(k), mid + 1, r, pos, Size);
    pushup(k);
}

void update2(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R)
    {
        sum[k] = cnt[k] = 0;
        lazy[k] = 1;
        return ;
    }
    pushdown(k);
    int mid = (l + r) >> 1;
    if(L <= mid) update2(ls(k), l, mid, L, R);
    if(R > mid) update2(rs(k), mid + 1, r, L, R);
    pushup(k);
}

pair<ll, ll> query(int k, int l, int r, int L, int R)
{
    if(L <= l && r <= R) return pair<ll, ll>(sum[k], cnt[k]);
    pushdown(k);
    int mid = (l + r) >> 1;
    if(R <= mid) return query(ls(k), l, mid, L, R);
    if(L > mid) return query(rs(k), mid + 1, r, L, R);
    pair<ll, ll> s1 = query(ls(k), l, mid, L, R), s2 = query(rs(k), mid + 1, r, L, R);
    return pair<ll, ll>(s1.first + s2.first , s1.second + s2.second );
}

int get(int k, int l, int r, int id)
{
    if(l == r) return l;
    pushdown(k);
    int mid = (l + r) >> 1;
    if(cnt[ls(k)] >= id) return get(ls(k), l, mid, id);
    else return get(rs(k), mid + 1, r, id - cnt[ls(k)]);
}

int getcnt(int k, int l, int r, int pos)
{
    int mid = (l + r) >> 1;
    pushdown(k);
    if(pos == mid) return cnt[ls(k)];
    if(pos < mid) return getcnt(ls(k), l, mid, pos);
    if(pos > mid) return cnt[ls(k)] + getcnt(rs(k), mid + 1, r, pos);
}

int main()
{
    n = read();
    for(int i = 1; i <= n; ++i) b[i] = X[i] = read() - i;
    Q = read();
    for(int i = 1; i <= Q; ++i)
    {
        T[i] = read(), G[i] = read();
        b[n + i] = G[i] = G[i] - T[i];
    }
    // for(int i = 1; i <= n; ++i) printf("%d ", X[i]);
    // printf("\n");
    // for(int i = 1; i <= Q; ++i) printf("%d ", G[i]);
    // printf("\n");
    // for(int i = 1; i <= n + Q; ++i) printf("%d ", b[i]);
    // printf("\n");
    sort(b + 1, b + n + Q + 1);
    int nn = unique(b + 1, b + n + Q + 1) - (b + 1);
    for(int i = 1; i <= n; ++i) x[i] = lower_bound(b + 1, b + nn + 1, X[i]) - b;
    for(int i = 1; i <= Q; ++i) g[i] = lower_bound(b + 1, b + nn + 1, G[i]) - b;
    for(int i = 1; i <= n; ++i) update1(1, 1, nn, x[i], 1);
    // for(int i = 1; i <= n; ++i) printf("%d ", x[i]);
    // printf("\n");
    // for(int i = 1; i <= Q; ++i) printf("%d ", g[i]);
    // printf("\n");
    // for(int i = 1; i <= n + Q; ++i) printf("%d ", b[i]);
    // printf("\n");
    ll ans = 0;
    for(int i = 1; i <= Q; ++i)
    {
        int pos = get(1, 1, nn, T[i]);
        if(pos == g[i]) continue;
        pair<ll, ll> now = pair<ll, ll>(0ll, 0ll);
        // printf("i = %d, T = %d, pos = %d, g = %d\n", i, T[i], pos, g[i]); 
        if(pos < g[i])
        {
            int CNT = getcnt(1, 1, nn, pos);
            now = query(1, 1, nn, pos + 1, g[i]);
            // printf("CNT = %d, sum = %lld, cnt = %lld\n", CNT, now.first , now.second );
            now.second += CNT - T[i] + 1, now.first += 1ll * (CNT - T[i] + 1) * b[pos];
            update1(1, 1, nn, pos, -(CNT - T[i] + 1));
            update2(1, 1, nn, pos + 1, g[i]);
            update1(1, 1, nn, g[i], now.second );
        }else
        {
            int CNT = 0;
            if(pos > 1) CNT = getcnt(1, 1, nn, pos - 1);
            now = query(1, 1, nn, g[i], pos - 1);
            // printf("CNT = %d, sum = %lld, cnt = %lld\n", CNT, now.first , now.second );
            now.second += T[i] - CNT, now.first += 1ll * (T[i] - CNT) * b[pos];
            update1(1, 1, nn, pos, -(T[i] - CNT));
            update2(1, 1, nn, g[i], pos - 1);
            update1(1, 1, nn, g[i], now.second );
        }
        // printf("now.first = %lld, second = %lld\n", now.first , now.second );
        ans += abs(b[g[i]] * now.second - now.first );
        // printf("add = %lld\n", abs(b[g[i]] * now.second - now.first ));
    }
    printf("%lld\n", ans);
    return 0;
}

G - Lexicographically Smallest Permutation

给定序列 \(A\) 和序列 \(p\),一次操作是用 \(A_{p_i}\) 替换 \(A_i\),问替换任意次后,字典序最小的 \(A\) 序列是什么?

容易发现对于每一个置换环,贪心的确定其在序列 \(A\) 上第一次出现的位置放哪个数即可。

对于第一个置换环,假设经过 \(x\) 次替换使得第一个数最小。

对于第二个置换环,按照每个数从小到大枚举,设需要 \(y_i\) 次替换使得第一个数是这个环中第 \(i\) 小的数。

设前面的置换环的长度的最小公倍数为 \(Y\),那么只需要满足 \(kY + x \equiv y_i (\mod len)\)

依次判断同余式能否成立即可。

至多每个数都被判断一次,判断一次是 \(\log\)\(\operatorname{EXgcd}\)

但是 \(\operatorname{lcm}\) 会很大,需要高精度。

posted @ 2024-09-16 23:50  梨愁浅浅  阅读(50)  评论(0编辑  收藏  举报