头图

好序列的个数

题目描述

现在你面前有一棵n个节点的树(全连通无环图)。树上的边只有2种颜色,红色或者黑色。现在还给你一个整数k,考虑下面这个k个节点的序列[a1, a2, ..., ak]。

[a1, a2, ..., ak]如果是”好序列“当且仅当满足下面的条件:

  1. 我们要走一条从a1开始到ak结束的路径。
  2. 从a1开始,到a2走一条a1到a2的最短路。然后从a2开始,继续走一条到a3的最短路,以此类推,最终到a(k-1)和ak。
  3. 走的路径中至少包含一条黑色的边。

总共有n^k(n的k次方种路径方案),那么有多少路径是“好序列”呢?这个值可能非常大,输出的结果对(10^9+7)取模就可以。

输入描述

第一行是2个整数n和k,其中(2 <= n <= 10^5, 2 <= k <= 100),n表示树的节点个数,k表示序列的长度。

下面n-1行,每行包含3个整数,u[i], v[i], w[i],其中1 <= u[i], v[i] <= n, w[i] = 0或1。u[i], v[i]表示这两个节点之间有一条边,w[i]表示这条边的颜色,其中0表示红色,1表示黑色。

输出描述

输出所有“好序列”的个数模(10^9+7)。

解题思路

好序列的数量 = 全部路径的数量 - 非好序列的数量 = 全部路径的数量 - 仅含红边的路径的数量。

代码实现

typedef long long ll;
const int N = 1e5 + 5, MOD = 1e9 + 7;

int h[N], e[N << 1], ne[N << 1], idx;
bool w[N << 1], st[N];

/**
 * 建图
 * @param a 边的起点
 * @param b 边的终点
 * @param c 边的权重,0红,1黑
 */
void add(int a, int b, bool c) {
    e[idx] = b, ne[idx] = h[a], w[idx] = c, h[a] = idx++;
}

/**
 * 快速幂
 * @return x^n % MOD
 */
ll kms(ll x, ll n) {
    ll res = 1 % MOD;
    while (n) {
        if (n & 1)res = res * x % MOD;
        x = x * x % MOD;
        n >>= 1;
    }
    return res;
}

/**
 * 求包含节点x、仅含红边、不含已访问红边的子图的白边的数量
 * @param x 节点编号
 * @param f 节点x从节点f拓展而来
 */
int dfs(int x, int f) {
    int res = 1;
    st[x] = true;
    for (int i = h[x]; i != -1; i = ne[i]) {
        int j = e[i];
        if (j != f && !st[j] && !w[i])res += dfs(j, x);
    }
    return res;
}


int main() {
    memset(h, -1, sizeof h);
    memset(st, 0, sizeof st);
    int n, k;
    scanf("%d%d", &n, &k);
    for (int i = 1, a, b, c; i < n; i++) {
        scanf("%d%d%d", &a, &b, &c);
        add(a, b, c);
        add(b, a, c);
    }
    ll res = 0;
    for (int i = 1; i <= n; i++)
        if (!st[i])
            res = (res + kms(dfs(i, -1), k)) % MOD;
    res = (kms(n, k) + MOD - res) % MOD;
    printf("%lld", res);
    return 0;
}

时间复杂度:$O(n)$。

空间复杂度:$O(n)$。

计算累计平方和

题目描述

给定一个32位int型正整数,我们定义如下操作,取其十进制各位数字的平方和,并不断重复这个操作。如果某次操作完成后得到的结果是1,则返回true;否则继续执行,直到证明永远不会得到结果为1,返回false。

输入描述

输入一个m(1<=m<=1000),表示查询组数。

接下来m行,每一行为一个32位int型正整数。

输出描述

对于每次查询,如果满足题目描述,则输出"true",反之输出"false" (不要输出引号)。

解题思路

在重复操作的过程中,如果出现循环,则说明永远无法得到结果。

代码实现

/**
 * 计算x的各位平方之和
 */
int cal(int x) {
    int res = 0, t;
    while (x) {
        t = x % 10, x /= 10;
        res += t * t;
    }
    return res;
}

int main() {
    int m, x;
    scanf("%d", &m);
    while (m--) {
        scanf("%d", &x);
        unordered_set<int> st;
        while (st.find(x) == st.end()) {
            if (x == 1)break;
            st.insert(x);
            x = cal(x);
        }
        printf(x == 1 ? "true\n" : "false\n");
    }
    return 0;
}

时间复杂度:$O(10^2*8)$,即 $O(1)$。各位平方之和最多 $10^2*8$ 种可能。

空间复杂度:$O(1)$。

版本升级判定

题目描述

给定两个版本号,只有在版本号更高的时候,才可以升级。「.」号作为分割符使用,版本号中只有数和.号。

输入描述

第一行为m(1<=m<=100000),表示测试组数,接下来m行,表示m次查询。

每行两个版本号,空格分隔。一个版本号中最多只会出现3个「.」。每个版本号中数字1<=x<=100。

输出描述

对于每一次查询,输出是否可以升级,是则输出"true",否则输出"false" (引号不要输出)。

解题思路

按题意进行判断即可。

代码实现

/**
 * 判断是否可以从s升级到t
 */
bool comp(const char *s, const char *t) {
    int i = 0, j = 0;
    while (s[i] != '\0' && t[j] != '\0') {
        int x = 0;
        while (s[i] != '\0' && s[i] != '.')x = x * 10 + s[i] - '0', i++;
        if (s[i] == '.')i++;
        int y = 0;
        while (t[j] != '\0' && t[j] != '.')y = y * 10 + t[j] - '0', j++;
        if (t[j] == '.')j++;
        if (x == y)continue;
        return x < y;
    }
    while (t[j] != '\0') {
        if (t[j] != '.' && t[j] != '0')return true;
        j++;
    }
    return false;
}

int main() {
    int m;
    char s[20], t[20];
    scanf("%d", &m);
    while (m--) {
        scanf("%s%s", s, t);
        printf(comp(s, t) ? "true\n" : "false\n");
    }
    return 0;
}

时间复杂度:$O(m)$。

空间复杂度:$O(m)$。

合并内容流

题目描述

合并两个内容流,实现隔4个插入1个,如果合并完还有剩下,则加内容流尾部。

输入描述

第1行表示第一种类型的内容,第2行表示第二种类型的内容,字符数量<=100,空格分隔,比如说

1 2 3 4 5 6 7 8 9
a b c

输出描述

合并两种内容流,输出

1 2 3 4 a 5 6 7 8 b 9 c

解题思路

模拟即可。

代码实现

int main() {
    const int N = 1e2 + 5;
    char s[2][N];
    scanf("%[^\n]\n%[^\n]", s[0], s[1]);
    int i = 0, j = 0, k = 0;
    while (true) {
        bool f = k % 5 == 4;
        int &idx = f ? j : i;
        printf("%c ", s[f][idx]);
        idx++;
        if (s[f][idx] == '\0') {
            printf("%s", s[!f] + (f ? i : j));
            break;
        };
        idx++, k++;
    }
    return 0;
}

时间复杂度:$O(1)$。

空间复杂度:$O(1)$。

END

详细题解:由于本平台不能完全支持Markdown语法,查看详细题解请移步至 ABC

文章文档:公众号 字节幺零二四 回复关键字可获取本文文档。

题目来源:快手2020校园招聘秋招笔试--工程A试卷

文章声明:题目来源 牛客 平台,如有侵权,请联系删除!


字节幺零二四
9 声望5 粉丝

talk is cheap, show me you code!