2024年中国传媒大学程序设计大赛(同步赛)解题报告(流水账版) | 珂学家

小苯的区间和疑惑

https://ac.nowcoder.com/acm/contest/77526/A


前言

alt


整体评价

整场还是挺nice,不是特别难,除了I题。J题是运气好,刚研究过区间gcd的性质,F题是启发式合并,不知道是不是正解,D题是一道很好的构造题,很妙。B题用了调和级数。其他的题,没啥印象了。倒是G题,感觉太套路,但是过的人有点少。


A. 小苯的区间和疑惑

思路: 前后缀拆解

n = int(input())

arr = list(map(int, input().split()))

if n == 1:
    print (arr[0])
    exit(0)

from math import inf
pre = [-inf] * n
suf = [-inf] * n

for i in range(n):
    if i == 0:
        pre[i] = max(0, arr[i])
    else:
        pre[i] = max(pre[i - 1] + arr[i], arr[i])
        pre[i] = max(0, pre[i])

for i in range(n - 1, -1, -1):
    if i == n - 1:
        suf[i] = max(0, arr[i])
    else:
        suf[i] = max(suf[i + 1] + arr[i], arr[i])
        suf[i] = max(0, suf[i])

        
res = [-inf] * n
res[0] = arr[0] + suf[1]
res[n - 1] = arr[n - 1] + pre[n - 2]
for i in range(1, n - 1):
    res[i] = suf[i + 1] + pre[i - 1] + arr[i]
    
print (*res, sep=' ')

B. 小苯的三元组

思路: 调和级数

推导

说明 , 且满足

所以这题的思路,就是维护每个数的因子个数和倍数个数

调和级数

n = int(input())

arr = list(map(int, input().split()))

mx = max(arr)

# 感觉是调和级数
from collections import Counter

cnt = [0] * (mx + 1)
for v in arr:
    cnt[v] += 1

hp1 = [0] * (mx + 1)
hp2 = [0] * (mx + 1)

# 预处理 因子和倍数
for i in range(1, mx + 1):
    cc = cnt[i]
    if cc == 0:
        continue
    j = 2
    while j * i <= mx:
        v = cnt[j * i]
        if v > 0:
            hp1[i] += v
            hp2[j * i] += cc
        j += 1
        
res = 0
for i in range(1, mx + 1):
    if cnt[i] == 0:
        continue
    v = cnt[i]
    res += v * v * v
    res += v * v * hp1[i]
    res += v * v * hp2[i]
    res += v * hp1[i] * hp2[i]
    
print (res)

C. 小红的 CUC

签到题

n = int(input())
if n < 3:
    print (-1)
else:
    print ("CUC" + ''.join(['A' for _ in range(n - 3)]))

D. 小红的矩阵构造(一)

思路: 构造

从排列的角度出发

先从n的行和列,出发,这样可以填满每个格子(填1) 然后从1的行和列出发,这样可以填满剩余的格子(填0)

然后去掉这两行两列之后,

就会出现,一个(n-2) * (n-2) 矩阵,也就是子问题。

即(n, 1) -> (n-1, 2) -> (n-2, 3) -> ....

大概是这个思路,我觉得这题很难。

# (n, 1), (n - 1, 2),... 
n = int(input())

arr = list(map(int, input().split()))
brr = list(map(int, input().split()))

vis = [[False] * n for _ in range(n)]
chess = [['0'] * n for _ in range(n)]


arrRev = [0] * (n + 1)
brrRev = [0] * (n + 1)

for i in range(n):
    arrRev[arr[i]] = i
    brrRev[brr[i]] = i
    

big, small = n, 1

while big >= small:
    idx = arrRev[big]
    for i in range(n):
        if not vis[idx][i]:
            vis[idx][i] = True
            chess[idx][i] = '1'
    idx2 = brrRev[big]
    for i in range(n):
        if not vis[i][idx2]:
            vis[i][idx2] = True
            chess[i][idx2] = '1'
    
    pdx = arrRev[small]
    for i in range(n):
        if not vis[pdx][i]:
            vis[pdx][i] = True
            
    pdx2 = brrRev[small]
    for i in range(n):
        if not vis[i][pdx2]:
            vis[i][pdx2] = True

    big-=1
    small+=1
    
for i in range(n):
    print (''.join(chess[i]))

E. 小红的矩阵构造(二)

思路:构造

按序构造即可

h, w, k = list(map(int, input().split()))

if k > (h - 1) * (w - 1):
    print (-1)
else:
    grids = [['0'] * w for _ in range(h)]
    
    grids[0][0] = '1'
    grids[1][0] = '1'
    for i in range(1, w):
        if k > 0:
            grids[0][i] = '1'
            grids[1][i] = '1'
            k -= 1
    
    row = 2
    while k > 0:
        grids[row][0] = '1'
        for i in range(1, w):
            if k > 0:
                grids[row][i] = '1'
                k -= 1
        row += 1
    
    for i in range(h):
        print (''.join(grids[i]))
    

F. 小红的子树乘积

思路:启发式合并(大的合并小的) + 数论

这题用java写,因为python的递归栈深度太小了。

如果子节点>=k了,其实父节点肯定>=k, 这边是可以优化的

import java.io.*;
import java.util.*;


public class Main {

    static class Solution {
        int n;
        long k;
        List<Integer>[]g;

        int[] dp;

        int solve(int n, long k, List<Integer> []g) {
            this.n = n;
            this.g = g;
            this.k = k;
            this.dp = new int[n + 1];

            dfs(1, -1);
            return Arrays.stream(dp).sum();
        }

        // 有一个数学公式
        Map<Integer, Integer> dfs(int u, int fa) {
            // *)
            Map<Integer, Integer> mp = new TreeMap<>();
            for (int v: g[u]) {
                if (v == fa) continue;
                Map<Integer,Integer> cs = dfs(v, u);
                if (dp[v] == 1) {
                    dp[u] = 1;
                    continue;
                }

                // 启发式合并
                if (cs.size() > mp.size()) {
                    Map<Integer, Integer> tmp = mp;
                    mp = cs;
                    cs = tmp;
                }
                for (Map.Entry<Integer, Integer> ent: cs.entrySet()) {
                    mp.merge(ent.getKey(), ent.getValue(), Integer::sum);
                }
            }
            if (dp[u] == 0) {
                int cu = u;
                for (int i = 2; i <= cu / i; i++) {
                    if (cu % i == 0) {
                        int cc = 0;
                        while (cu % i == 0) {
                            cu /= i;
                            cc++;
                        }
                        mp.merge(i, cc, Integer::sum);
                    }
                }
                if (cu > 1) {
                    mp.merge(cu, 1, Integer::sum);
                }

                long r = 1;
                for (Map.Entry<Integer, Integer> ent: mp.entrySet()) {
                    r *= (ent.getValue() + 1);
                    if (r >= k) {
                        dp[u] = 1;
                        break;
                    }
                }
            }
            return mp;
        }

    }

    public static void main(String[] args) {
        AReader sc = new AReader();
        int n = sc.nextInt();
        long k = sc.nextLong();

        List<Integer>[] g = new List[n + 1];
        Arrays.setAll(g, x->new ArrayList<>());

        for (int i = 0; i < n - 1; i++) {
            int u = sc.nextInt(), v = sc.nextInt();
            g[u].add(v);
            g[v].add(u);
        }

        Solution solution = new Solution();
        int res = solution.solve(n, k, g);
        System.out.println(res);
        
    }

    static
    class AReader {
        private BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
        private StringTokenizer tokenizer = new StringTokenizer("");
        private String innerNextLine() {
            try {
                return reader.readLine();
            } catch (IOException ex) {
                return null;
            }
        }
        public boolean hasNext() {
            while (!tokenizer.hasMoreTokens()) {
                String nextLine = innerNextLine();
                if (nextLine == null) {
                    return false;
                }
                tokenizer = new StringTokenizer(nextLine);
            }
            return true;
        }
        public String nextLine() {
            tokenizer = new StringTokenizer("");
            return innerNextLine();
        }
        public String next() {
            hasNext();
            return tokenizer.nextToken();
        }
        public int nextInt() {
            return Integer.parseInt(next());
        }

        public long nextLong() {
            return Long.parseLong(next());
        }

//        public BigInteger nextBigInt() {
//            return new BigInteger(next());
//        }
        // 若需要nextDouble等方法,请自行调用Double.parseDouble包装
    }
}

G. 小红的独特区间

思路:三指针

太套路了,这题,尤其在牛客上。

# 三指针就行

n = int(input())
arr = list(map(int, input().split()))

from collections import Counter

cnt1 = Counter()
cnt2 = Counter()

ptr1 = 0
ptr2 = 0

x1, x2 = 0, 0

res = 0
for i in range(n):
    v = arr[i]
    if cnt1[v] == 0:
        x1 += 1
    if cnt2[v] == 0:
        x2 += 1
    cnt1[v] += 1
    cnt2[v] += 1
        
    while ptr1 <= i and x1 > 3:
        v1 = arr[ptr1]
        if cnt1[v1] == 1:
            x1 -= 1
        cnt1[v1] -= 1
        ptr1 += 1
        
    while ptr2 <= i and x2 > 2:
        v1 = arr[ptr2]
        if cnt2[v1] == 1:
            x2 -= 1
        cnt2[v1] -= 1
        ptr2 += 1
        
    res += (ptr2 - ptr1)
    
print (res)

H. 小红的生物实验

思路: BFS

阅读理解题,有点绕,但是很板

h, w = list(map(int, input().split()))

chess = []
for _ in range(h):
    chess.append(list(input()))
    
from collections import deque

vis = [[False] * w for _ in range(h)]

deq = deque()
for i in range(0, w):
    if chess[0][i] == '.':
        deq.append((0, i))
    vis[0][i] = True        
    if chess[h - 1][i] == '.':
        deq.append((h - 1, i))
    vis[h - 1][i] = True    

for i in range(1, h - 1):
    if chess[i][0] == '.':
        deq.append((i, 0))
    vis[i][0] = True    
    if chess[i][w - 1] == '.':
        deq.append((i, w - 1))
    vis[i][w - 1] = True    


while len(deq) > 0:
    y, x = deq.popleft()
    for (dy, dx) in [(-1, 0), (1, 0), (0, -1), (0, 1)]:
        ty = y + dy
        tx = x + dx
        if 0 <= tx < w and 0 <= ty < h:
            if vis[ty][tx]:
                continue
            if chess[ty][tx] == '.':
                deq.append((ty, tx))
                vis[ty][tx] = True
            else:
                vis[ty][tx] = True

for i in range(h):
    for j in range(w):
        if vis[i][j]:
            chess[i][j] = '.'

for i in range(h):
    print (''.join(chess[i]))

J. 小红种树(二)

思路: 枚举 + 区间gcd

区间gcd的性质为

枚举最终的高度x

那么可得

转换为

除了第一项,后面其实个是常量,可以预处理

为了应对负值的问题,可以把arr进行排序,从大到小排序。

最后的结果为 k的因子个数

n, m = list(map(int, input().split()))

# 考察区间gcd
arr = list(map(int, input().split()))
arr.sort(reverse=True)

from math import gcd

# 预处理区间gcd
g = 0
for i in range(1, n):
    g = gcd(g, arr[i - 1] - arr[i])


# 预处理1000以内的质数
primes = []
vis = [False] * (1001)
for i in range(2, 1001):
    if not vis[i]:
        primes.append(i)
        for j in range(i * i, 1001, i):
            vis[j] = True

# 缓存优化
cache = {}
def compute(v):
    cv = v
    if v in cache:
        return cache[v]
    # 质因子拆解
    res = 1
    for i in range(len(primes)):
        if cv < primes[i]:
            break
        if cv % primes[i] == 0:
            cc = 0
            while cv % primes[i] == 0:
                cv = cv // primes[i]
                cc += 1
            res *= (cc + 1)
    # 超过1000,最多一个质因子
    if cv > 1:
        res *= 2
    cache[v] = res
    return res

res = 0
for i in range(arr[0], m + 1):
    gc = gcd(g, i - arr[0])
    res += compute(gc)
    
print (res)

写在最后

alt

全部评论
D题有简单的构造方法,不用子问题。 把第一行放满,然后从第二行开始从左往右放,每次多1个矩形。 k/(m-1)是要放满的行数,k%(m-1)是最后一行要放的个数
1 回复 分享
发布于 03-20 19:02 浙江
芙芙好评
1 回复 分享
发布于 03-21 16:12 北京

相关推荐

牛客410815733号:这是什么电影查看图片
点赞 评论 收藏
分享
12 1 评论
分享
牛客网
牛客企业服务