ABC343 A-G

本文详细分析了解决AtCoderBeginnerContest343中的五道题目,包括数值计算、图论、回文数查找、分数多样性统计、空间几何问题、动态规划等,展示了编程思路和代码实现。
摘要由CSDN通过智能技术生成

AtCoder Beginner Contest 343 - AtCoder

思路全出了,手搓全唐了,好消息是前几题手很快,上分了

A - Wrong Answer

题意:

输出一个不等与A+B的范围在0到9内的整数

代码:

void solve()
{
    int a, b;
    scanf("%d%d", &a, &b);
    if (a + b)printf("0\n");
    else printf("1\n");
}

B - Adjacency Matrix

题意:

给出一张有N个点的简单无向图,邻接表中的 Ai,j = 1表示 i, j 之间有边相连,对于每个点输出所有直接与它相连的点的编号

代码:

void solve()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
        vector<int>v;
        for (int j = 1, x; j <= n; ++j)
        {
            scanf("%d", &x);
            if (x)v.push_back(j);
        }
        for (auto j : v)printf("%d ", j);
        printf("\n");
    }
}

C - 343

题意:

输出小于N的最大的回文立方数

题解:

O(N^{_{\frac{1}{3}}})暴力枚举所有立方数并暴力判断回文数即可

bool check(LL x)//判断x是不是回文数
{
    vector<int>v;
    while (x)
        v.push_back(x % 10), x /= 10;
    int n = v.size() - 1;
    for (int i = 0; i <= n; ++i)
        if (v[i] != v[n - i])return 0;
    return 1;
}
void solve()
{
    LL n, mx = 1;
    scanf("%lld", &n);
    for (LL i = 1; i * i * i <= n; ++i)//枚举立方数
        if (check(i * i * i))mx = i * i * i;
    printf("%lld\n", mx);
}

D - Diversity of Scores

题意:

有N人参加比赛,编号分别为1到N,每人都有一个积分,这个积分初始为0,这些分数将进行共计T次变化,第 i 次变化Ai, Bi表示第Ai个人获得了Bi分,你需要在每次分数发现变化后,输出当前不同的分数的数量

题解:

数组A维护一下每个人的分数,map<x,y>表示获得x分的人数共有y人,若每次修改前mp[A[x]] = 1则本次修改会使分数为A[x]的人数变为0,此时删除A[x]即可,具体维护方式见代码,修改之后的答案即mp.size(),注意数据范围爆int

LL a[N];
map<LL, int>mp;
void solve()
{
    int n, q;
    scanf("%d%d", &n, &q);
    mp[0] = n;
    while (q--)
    {
        int x, y;
        scanf("%d%d", &x, &y);
        if (--mp[a[x]] == 0)
            mp.erase(a[x]);
        a[x] += y;
        ++mp[a[x]];
        printf("%d\n", mp.size());
    }
}

E - 7x7x7

题意:

在三维空间中有3个7x7x7的正方体,问是否存在一种放置方式使得同时被三个正方体包含的区域体积为V3,同时仅被两个正方体包含的区域体积为V2,仅被一个正方体包含的区域体积为V1,若存在输出三个正方体的位置

题解:

设第一个正方体的位置恒为(0, 0, 0),暴力枚举第二、三个正方体的位置,check每种放置方法是否合法即可,具体check方式见代码

对于枚举位置,显然让三个正方体离太远没有意义,我们只需要枚举所有能使得三个正方体相交/至少有一个面重合的最大范围即可,则我们可以枚举第二个正方体的三个维度的坐标值在(0, 7)范围,第三个正方体的坐标值在(-7, 14)范围即可(不知道还能不能更小)

int get_d(int x, int tx)//两个正方体的重合部分x/y/z方向上的边长
{
    return max(0, min(x, tx) + 7 - max(x, tx));
}
int get_d(int sx, int x, int tx)//三个正方体的重合部分x/y/z方向上的边长
{
    return max(0, min({ sx,x,tx }) + 7 - max({ sx,x,tx }));
}
int fun(int ax, int ay, int az, int bx, int by, int bz)//两个7x7x7的正方体的重合体积
{
    int dx = get_d(ax, bx), dy = get_d(ay, by), dz = get_d(az, bz);
    return dx * dy * dz;
}
void solve()
{
    int v1, v2, v3;
    scanf("%d%d%d", &v1, &v2, &v3);
    for (int x = 0; x <= 7; ++x)
    {
        for (int y = 0; y <= 7; ++y)
        {
            for (int z = 0; z <= 7; ++z)
            {
                for (int tx = -7; tx <= 14; ++tx)
                {
                    for (int ty = -7; ty <= 14; ++ty)
                    {
                        for (int tz = -7; tz <= 14; ++tz)
                        {
                            int dx = get_d(0, x, tx), dy = get_d(0, y, ty), dz = get_d(0, z, tz);
                            int tv3 = dx * dy * dz;
                            if (tv3 != v3)continue;
                            int tv2 = fun(0, 0, 0, x, y, z) + fun(0, 0, 0, tx, ty, tz) + fun(x, y, z, tx, ty, tz) - 3 * tv3;//容斥
                            if (tv2 != v2)continue;
                            int tv1 = 3 * 7 * 7 * 7 - 3 * tv3 - 2 * tv2;//容斥
                            if (tv1 != v1)continue;
                            printf("Yes\n");
                            printf("0 0 0 %d %d %d %d %d %d\n", x, y, z, tx, ty, tz);
                            return;
                        }
                    }
                }
            }
        }
    }
    printf("No\n");
}

F - Second Largest Query

题意:

给出一个长度为N的数组A,对该数组进行T次操作:

1 p x:修改Ap的值为x

2 l r:查询区间l, r内的次大值的数量

题解:

线段树维护即可,具体见代码

#define ls(i) (i<<1)
#define rs(i) (i<<1|1)
#define mx(i,j) (tr[i].mx[j])
#define cnt(i,j) (tr[i].cnt[j])
struct node
{
    int mx[2], cnt[2];//最大值,次大值;最大值的数量,次大值的数量
}tr[N << 2];
node push_up(node x, node y)
{
    map<int, int>mp;
    mp[x.mx[0]] += x.cnt[0];
    mp[x.mx[1]] += x.cnt[1];
    mp[y.mx[0]] += y.cnt[0];
    mp[y.mx[1]] += y.cnt[1];
    auto it = mp.rbegin();
    node res = { {it->first,next(it)->first},{it->second,next(it)->second} };
    return res;
}
void build(int l, int r, int i)
{
    if (l == r)
    {
        scanf("%d", &mx(i, 0));
        cnt(i, 0) = 1;
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, ls(i));
    build(mid + 1, r, rs(i));
    tr[i] = push_up(tr[ls(i)], tr[rs(i)]);
}
void updata(int pos, int data, int l, int r, int i)
{
    if (l == r)
    {
        mx(i, 0) = data;
        return;
    }
    int mid = l + r >> 1;
    if (pos <= mid)updata(pos, data, l, mid, ls(i));
    else updata(pos, data, mid + 1, r, rs(i));
    tr[i] = push_up(tr[ls(i)], tr[rs(i)]);
}
node query(int ql, int qr, int l, int r, int i)
{
    if (ql <= l && r <= qr)
        return tr[i];
    int mid = l + r >> 1;
    node res = { {0,0},{0,0} };
    if (ql <= mid)res = push_up(res, query(ql, qr, l, mid, ls(i)));
    if (qr > mid)res = push_up(res, query(ql, qr, mid + 1, r, rs(i)));
    return res;
}
void solve()
{
    int n, q;
    scanf("%d%d", &n, &q);
    build(1, n, 1);
    while (q--)
    {
        int op, x, y;
        scanf("%d%d%d", &op, &x, &y);
        if (op == 1)
            updata(x, y, 1, n, 1);
        else
            printf("%d\n", query(x, y, 1, n, 1).cnt[1]);
    }

}

G - Compress Strings

题意:

给出N个字符串S,求字符串T的最小长度使得所有的Si都是T的子串

题解:

相当于是像拼积木一样拼接所有字符串,当Si的后缀等于Sj的前缀时就可以拼接Si与Sj的同时删去Sj的那一段相同的前缀,同时当Sj是Si的子串时则可以直接把Sj塞进Si里,不产生更多的长度,求最小的拼接之后的总长度

对于求Si的后缀与Sj的前缀的最大匹配长度和Sj是否是Si的子串这类字符串匹配问题可以用Z函数、KMP或者哈希处理

对于求最小总长度,我们可以发现N很小,因此我们可以使用状压dp,dp[f][i]表示已经拼接的集合为f,并且当前的拼接字符串的后缀为Si的后缀时的最小长度,此时往拼接字符串末尾拼接字符Sj存在两种转移:

1、Sj是Si的子串,则往集合中加入j,拼接字符串后缀仍为Si,不产生多余花费;

2、Sj不是Si的子串,往集合中加入j,拼接字符串后后缀变更为Sj,花费为 Sj的长度 - Si后缀与Sj前缀的最大匹配长度。

从集合中元素数量从少到多枚举状态转移即可

思路很快,但搓代码搓的很慢,比赛结束几分钟才搓完然后爆wa,赛后20分钟才调完的bug,写的太唐了,用的字符串哈希判断的字符串匹配,赛后顺便整理了一下模板,感觉这个板子写的挺还帅的

const LL N = 2e1 + 10, INF = 0x3f3f3f3f;
//个人手搓的多模哈希板子,感觉挺好使的
const int LEN = 2e5 + 10, H = 3, L = 1e18, R = 2e9;//最长的字符串长度, 哈希数量, 随机数生成范围
mt19937_64 rnd(random_device{}());//生成随机数
uniform_int_distribution<int> dist(L, R);
typedef array<LL, H> Hash;
Hash P, mod, p[LEN];
Hash operator*(Hash x, Hash y)//重载运算符是好东西
{
    Hash res;
    for (int i = 0; i < H; ++i)
        res[i] = x[i] * y[i] % mod[i];
    return res;
}
Hash operator+(Hash x, Hash y)
{
    Hash res;
    for (int i = 0; i < H; ++i)
        res[i] = (x[i] + y[i]) % mod[i];
    return res;
}
Hash operator-(Hash x, Hash y)
{
    Hash res;
    for (int i = 0; i < H; ++i)
        res[i] = (x[i] - y[i] + mod[i]) % mod[i];
    return res;
}
void fill(Hash& hs, LL x)
{
    for (auto& i : hs)i = x;
}
void init()
{
    for (int i = 0; i < H; ++i)
        P[i] = dist(rnd), mod[i] = dist(rnd);
    fill(p[0], 1);
    for (int i = 1; i < LEN; ++i)
        p[i] = p[i - 1] * P;
}
vector<Hash> build_hash(string& str)
{
    Hash t;
    fill(t, 0);
    vector<Hash>hs{ t };
    for (auto& c : str)
    {
        fill(t, c);
        hs.push_back(hs.back() * P + t);
    }
    return hs;
}
Hash get_hash(vector<Hash>& hs, int l, int r)
{
    return hs[r] - hs[l - 1] * p[r - l + 1];
}
//哈希板子
string s[N];
vector<Hash>h[N];
int e[N][N], dp[1 << 20][N];
void solve()
{
    init();
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i)
    {
        cin >> s[i];
        h[i] = build_hash(s[i]);
    }
    memset(e, 0x3f, sizeof e);
    for (int i = 1; i <= n; ++i)//i包含j
    {
        for (int j = 1; j <= n; ++j)if (i != j && s[i].size() >= s[j].size())
        {
            Hash hj = get_hash(h[j], 1, s[j].size());
            for (int l = 1; l + s[j].size() - 1 <= s[i].size(); ++l)
            {
                if (get_hash(h[i], l, l + s[j].size() - 1) == hj)
                {
                    e[i][j] = 0;
                    break;
                }
            }
        }
    }
    for (int i = 1; i <= n; ++i)//i拼接j的代价
    {
        for (int j = 1; j <= n; ++j)if (i != j)
        {
            int len = 0;//相同部分长度
            for (int t = 1; t <= s[i].size() && t <= s[j].size(); ++t)
            {
                if (get_hash(h[i], s[i].size() - t + 1, s[i].size()) == get_hash(h[j], 1, t))
                    len = t;
            }
            e[i][j] = min(e[i][j], (int)s[j].size() - len);
        }
    }
    vector<int>v[21];//预处理所有元素数量为i的集合vi
    for (int f = 1; f < 1 << 20; ++f)
        v[bitset<21>(f).count()].push_back(f);
    memset(dp, 0x3f, sizeof dp);
    for (int i = 1; i <= n; ++i)//初始化dp数组
        dp[1 << i - 1][i] = s[i].size();
    for (int s = 1; s < n; ++s)
    {
        for (auto f : v[s])
        {
            for (int u = 1; u <= n; ++u)if ((f >> u - 1) & 1)
            {
                for (int tv = 1; tv <= n; ++tv)if (!((f >> tv - 1) & 1))
                {
                    if (e[u][tv] == 0)dp[f + (1 << tv - 1)][u] = min(dp[f + (1 << tv - 1)][u], dp[f][u]);
                    else dp[f + (1 << tv - 1)][tv] = min(dp[f + (1 << tv - 1)][tv], dp[f][u] + e[u][tv]);
                }
            }
        }
    }
    int ans = INF;
    for (int i = 1; i <= n; ++i)
        ans = min(ans, dp[(1 << n) - 1][i]);
    printf("%d\n", ans);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值