ABC343

算法题解:涵盖图、线段树、DP等

A - Wrong Answer

构造一个0…9内的ccc
使得a+b≠ca+b \neq ca+b=c


方法:
a,b都为0时取1
否则取0

B - Adjacency Matrix

图的01矩阵,求每个点uuu直接连接的点


#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;


int a[101][101];
int n;

int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n;
    for (int i = 1; i <= n; ++i) {
        for (int j = 1; j <= n; ++j) {
            cin >> a[i][j];
            if (a[i][j]) printf("%d ", j);
        }
        printf("\n");
    }
    return 0;
}

C - 343

求小于N的最大立方回文数


暴力

D - Diversity of Scores

N个数一开始都是0
给出T次修改,每次可以对位置i的数修改成AiA_iAi
最后求修改完之后有几个不同的值


维护每个数出现的次数,1变为0时和从0变为1时要更新结果

E - 7x7x7

构造一种摆放方式,摆放三个777的立方体,在空间中占据的格子被一个立方体占据的体积为V1V_1V1,两个立方体占据的体积为V2V_2V2,三个为V3V_3V3


很bt的题,搞不出来。
题解是暴力做,还没看懂。

F - Second Largest Query

单点更新,区间查询第二小的数出现的个数


看到这种描述就知道是线段树
贴一下我的代码 719ms,里面用了个set排序,维护第二小的个数比较麻烦。

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

const int N = 200020;

struct Node {
    int l, r;
    int mx, smx;	// max, second max
    int mxcnt, smxcnt;
};
Node tr[N << 2];
int n, q;
int a[N];
const int inf = 1 << 29;

#define lu (u * 2)
#define ru (u * 2 + 1)

void push_up(int u) {
    int x0 = tr[lu].mx, x1 = tr[lu].smx;
    int y0 = tr[ru].mx, y1 = tr[ru].smx;
    set<int, greater<int>> res;
    res.insert(x0); res.insert(x1); res.insert(y0); res.insert(y1);
    auto it = res.begin();
    tr[u].mx = *it;
    it++;
    tr[u].smx = *it;
    tr[u].mxcnt = tr[u].smxcnt = 0;
    if (tr[u].mx == x0) tr[u].mxcnt += tr[lu].mxcnt;
    if (tr[u].mx == y0) tr[u].mxcnt += tr[ru].mxcnt;
    if (tr[u].smx == x0) tr[u].smxcnt += tr[lu].mxcnt;
    if (tr[u].smx == y0) tr[u].smxcnt += tr[ru].mxcnt;
    if (tr[u].smx == x1) tr[u].smxcnt += tr[lu].smxcnt;
    if (tr[u].smx == y1) tr[u].smxcnt += tr[ru].smxcnt;
}

void build(int l, int r, int u) {
    tr[u].l = l, tr[u].r = r;
    if (l == r) {
        tr[u].mx = a[l];
        tr[u].smx = 0;
        tr[u].mxcnt = 1, tr[u].smxcnt = 1;
        return;
    }
    int mi = (l + r) / 2;
    build(l, mi, lu);
    build(mi + 1, r, ru);
    push_up(u);
}

void update(int p, int u, int v) {
    if (tr[u].l == tr[u].r && tr[u].l == p) {
        tr[u].mx = v;
        tr[u].smx = 0;
        tr[u].mxcnt = 1, tr[u].smxcnt = 1;
        return;
    }
    int mi = (tr[u].l + tr[u].r) / 2;
    if (p <= mi) {
        update(p, lu, v);
    }
    else {
        update(p, ru, v);
    }
    push_up(u);
}

Node query(int l, int r, int u) {
    if (l <= tr[u].l && tr[u].r <= r) {
        return tr[u];
    }
    int mi = (tr[u].l + tr[u].r) / 2;
    set<int, greater<int>> res;
    if (r <= mi) {
        Node t = query(l, r, lu);
        return t;
    }
    if (l > mi) {
        Node t = query(l, r, ru);
        return t;
    }
    Node lt = query(l, r, lu), rt = query(l, r, ru);
    Node ret{ 0, 0 };
    int x0 = lt.mx, x1 = lt.smx;
    int y0 = rt.mx, y1 = rt.smx;
    res.insert(x0); res.insert(x1); res.insert(y0); res.insert(y1);
    auto it = res.begin();
    ret.mx = *it;
    it++;
    ret.smx = *it;
    ret.mxcnt = ret.smxcnt = 0;
    if (ret.mx == x0) ret.mxcnt += lt.mxcnt;
    if (ret.mx == y0) ret.mxcnt += rt.mxcnt;
    if (ret.smx == x0) ret.smxcnt += lt.mxcnt;
    if (ret.smx == y0) ret.smxcnt += rt.mxcnt;
    if (ret.smx == x1) ret.smxcnt += lt.smxcnt;
    if (ret.smx == y1) ret.smxcnt += rt.smxcnt;
    return ret;
}

int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n >> q;
    for (int i = 1; i <= n; ++i) cin >> a[i];
    build(1, n, 1);
    for (int i = 1; i <= q; ++i) {
        int op;
        cin >> op;
        if (op == 1) {
            int p, x;
            cin >> p >> x;
            update(p, 1, x);
        }
        else {
            int l, r;
            cin >> l >> r;
            Node ret = query(l, r, 1);
            int ans = ret.smx == 0 ? 0 : ret.smxcnt;
            printf("%d\n", ans);
        }
    }
    return 0;
}

G - Compress Strings

题目很简单,有NNN(N≤20)(N \leq 20)(N20)字符串
求最短的字符串sss能全部包含这些字符串


首先把那些互相包含在内的情况去掉。
最短的情况一定是前面字符串的后缀和后面的字符串前缀相同。
问题就是求一个1…N的排列,使得拼接完成后的总长度最短。这是个典型的排列状态dp问题。
现在的难点是两个字符串拼接后最短的新字符串长度。换言之需要求前面字符串的后缀和后面字符串的前缀最大相同的长度。
例如
| snuke | kensho |
最大相同长度是2
我们可以把两个字符串反着拼起来:kenshosnuke,如何求这个ke?
如果熟悉kmp算法,会发现ke就是next数组想要求的东西。
但是要注意不需要完全拼成字符串,因为最大的相同长度不会超过两个字符串长度的最小值。
而且你的答案不会超过两个字符串长度的最大值,所以在中间我们加个特殊字符:
kensh#snuke

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <cmath>
#include <map>
#include <set>
#include <vector>
#include <queue>
#include <stack>
#include <unordered_map>
#include <algorithm>

using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
typedef pair<ll, ll> pll;
typedef vector<int> vi;

int f[21][21];
int n;
string ss[21];
vector<string> used;
bool v[21];
vector<vi> ns;
int g[1 << 20][20];


vi get_nxt(const string& s) {
    int n = s.length();
    vi nxt(n + 1, 0);
    for (int i = 1; i < n; ++i) {
        int j = nxt[i - 1];
        if (s[i] == s[j]) nxt[i] = j + 1;
        else {
            while (j) {
                j = nxt[j - 1];
                if (s[i] == s[j]) {
                    nxt[i] = j + 1;
                    break;
                }
            }
        }
    }
    return nxt;
}


bool contain(int i, int j, const vi &nxt) {
    if (ss[i].size() < ss[j].size()) return false;
    const string& s = ss[i];
    const string& t = ss[j];
    int n = s.size(), m = t.size();
    i = 0, j = 0;
    while (i < n) {
        if (s[i] == t[j]) {
            i++, j++;
        }
        else {
            if (j == 0) i++;
            else {
                j = nxt[j - 1];
            }
        }
        if (j == m) 
            return true;
    }
    return false;
}


void con(int i, int j) {
    int n = used[i].length(), m = used[j].length();
    int l = min(n, m);
    string si, sj;
    if (n > l) {
       si = used[i].substr(0, l);
    }
    else {
        si = used[i];
    }
    if (m > l) {
        sj = used[j].substr(m - l, l);
    }
    else {
        sj = used[j];
    }
    string s = si + '#' + sj;
    vi nxt = get_nxt(s);
    n = s.size();
    f[j][i] = nxt[n - 1];
}


int main(){
    //freopen("in.txt", "r", stdin);
    cin >> n;
    set<string> sset;
    for (int i = 0; i < n; ++i) {
        string t;
        cin >> t;
        sset.insert(t);
    }
    // 完全相同的情况先处理
    n = sset.size();
    int idx = 0;
    for (string s : sset) {
        ss[idx++] = s;
    }
    for (int i = 0; i < n; ++i) {
        vi nxt = get_nxt(ss[i]);
        for (int j = 0; j < n; ++j) {
            if (i != j && contain(j, i, nxt)) {
                v[i] = 1;
                break;
            }
        }
    }
    // 被包含的字符串不算
    for (int i = 0; i < n; ++i) {
        if (!v[i]) used.push_back(ss[i]);
    }
    n = used.size();
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (i != j) {
                con(i, j);
            }
        }
    }
    memset(g, 0x33, sizeof(g));
    for (int i = 0; i < n; ++i)
        g[1 << i][i] = used[i].size();
    for (int m = 1; m < (1 << n); ++m) {
        for (int j = 0; j < n; ++j) {
            if ((m >> j) & 1) {
                for (int i = 0; i < n; ++i) {
                    if ((m >> i) & 1) continue;
                    int nm = m | (1 << i);
                    g[nm][i] = min(g[nm][i], g[m][j] + int(used[i].size()) - f[j][i]);
                    // 这里忘记改成 used[i].size()了,原来是ss[i].size(),WA了两发
                }
            }
        }
    }
    int msk = (1 << n) - 1;
    int ans = g[msk][0];
    for (int i = 0; i < n; ++i) ans = min(g[msk][i], ans);
    printf("%d\n", ans);
    return 0;
}

有同学拿hash来计算最大相同长度,也是不错的思路

<think>根据用户需求,我们需要找出100到1000之间的水仙花数(Narcissistic number),也称为自幂数或阿姆斯特朗数(Armstrong number)。水仙花数是指一个n位数,其各位数字的n次方之和等于该数本身。对于三位数而言,即满足:$abc = a^3 + b^3 + c^3$,其中$a$、$b$、$c$分别是该数的百位、十位和个位数字。 ### 水仙花数定义 对于一个三位数$n$,设其百位数为$a$,十位数为$b$,个位数为$c$,则$n$满足: $$ n = a^3 + b^3 + c^3 $$ ### 实现思路 1. **遍历100到999之间的所有整数**(100到1000之间,不包括1000)。 2. **分离数字的各位**:对于每个整数,分别获取其百位、十位和个位数字。 3. **判断条件**:计算各位数字的立方和,并与原数比较。 4. **输出满足条件的数**:如果相等,则打印该数。 ### 参考代码实现(C语言) 参考用户提供的引用[1]中的C语言代码框架,我们实现如下: ```c #include <stdio.h> #include <math.h> // 使用pow函数计算立方 // 判断是否为水仙花数的函数 int isNarcissistic(int num) { int original = num; int a = num / 100; // 百位数 num %= 100; // 去除百位 int b = num / 10; // 十位数 int c = num % 10; // 个位数 // 计算立方和 int sum = pow(a, 3) + pow(b, 3) + pow(c, 3); return (sum == original); } int main() { printf("100到1000之间的水仙花数有:\n"); for (int i = 100; i < 1000; i++) { if (isNarcissistic(i)) { printf("%d ", i); } } return 0; } ``` ### 代码解释 1. **分离各位数字**: - 百位数:通过整数除法`num/100`得到。 - 十位数:先用`num %= 100`得到后两位,再除以10得到十位。 - 个位数:用模10运算`num % 10`得到。 2. **立方和计算**:使用`pow`函数计算各位数字的立方(也可以直接写`a*a*a`)。 3. **判断与输出**:在主函数中遍历100到999,调用判断函数,输出满足条件的数。 ### 运行结果 该程序将输出所有符合条件的三位数水仙花数。已知的三位数水仙花数有: - $153$($1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153$) - $370$($3^3 + 7^3 + 0^3 = 27 + 343 + 0 = 370$) - $371$($3^3 + 7^3 + 1^3 = 27 + 343 + 1 = 371$) - $407$($4^3 + 0^3 + 7^3 = 64 + 0 + 343 = 407$) ### 注意事项 - **范围设定**:注意循环范围为`i=100`到`i<1000`(即100~999)。 - **效率问题**:由于三位数数量较少,直接遍历即可。无需优化。 - **pow函数的使用**:`pow`函数返回浮点数,但由于输入是整数且立方也是整数,所以可以直接转换为整数比较。也可以直接用乘法(如`a*a*a`)避免类型转换问题。 ### 扩展思考 1. **其他位数的水仙花数**:可扩展到四位数(如1634)或更多位数。 2. **性能优化**:对于更多位数的计算,可以预先计算每个数字的n次方值并存储,以减少重复计算。 §§相关问题§§ 1. 如何计算四位数中的水仙花数(如1634)? 2. 水仙花数在密码学或数学中有哪些应用? 3. 是否存在无限多个水仙花数? </think>### 水仙花数计算(100-1000) 水仙花数(Narcissistic number)是指一个 $n$ 位数,其各位数字的 $n$ 次方之和等于该数本身。对于三位数而言,其定义式为: $$ abc = a^3 + b^3 + c^3 $$ 其中: - $a$ 是百位数字 - $b$ 是十位数字 - $c$ 是个位数字 #### 实现思路 1. **遍历100-999的所有整数** 2. **分离各位数字**: - 百位 $a = num // 100$ - 十位 $b = (num // 10) \% 10$ - 个位 $c = num \% 10$ 3. **验证水仙花条件**:$num == a^3 + b^3 + c^3$ #### 完整代码实现(Python) ```python print("100到1000之间的水仙花数:") for num in range(100, 1000): # 100-999的闭区间 a = num // 100 # 百位数 b = (num // 10) % 10 # 十位数 c = num % 10 # 个位数 # 验证水仙花条件 if num == a**3 + b**3 + c**3: print(f"{num} = {a}^3 + {b}^3 + {c}^3") ``` #### 代码解析 1. **数字分离**: - 百位:整除以100(如 `371//100=3`) - 十位:先整除10再取模10(如 `371//10=37` → `37%10=7`) - 个位:直接取模10(如 `371%10=1`) 2. **立方计算**:使用 `**` 运算符(`a**3` 表示 $a^3$) 3. **条件验证**:严格满足 $num = a^3 + b^3 + c^3$ #### 运行结果 ``` 100到1000之间的水仙花数: 153 = 1^3 + 5^3 + 3^3 370 = 3^3 + 7^3 + 0^3 371 = 3^3 + 7^3 + 1^3 407 = 4^3 + 0^3 + 7^3 ``` #### 数学验证(以153为例) $$ 153 = 1^3 + 5^3 + 3^3 = 1 + 125 + 27 = 153 $$ 符合水仙花数定义[^1]。 --- ### 拓展说明 1. **算法复杂度**:由于只遍历900个数字(100-999),时间复杂度为 $O(n)$,效率极高 2. **边界处理**:范围 `range(100, 1000)` 包含100但不包含1000,符合要求 3. **数学特性**:三位水仙花数仅有4个(153, 370, 371, 407),该结论已被证明[^1]
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值