牛客周赛 Round 76题解

牛客周赛 Round 76 题解

https://ac.nowcoder.com/acm/contest/99990

A.小红出题

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

小红每周一到周五上班,工作日每天都要出 3 3 3 道题目。
已知小红入职当天为星期一,并且已经在职 n n n 天。
请问她总共出了几道题目?

输入描述:

第一行有一个整数 n   (   1 ≤ n ≤ 1 0 3   ) n\ (\ 1 \leq n \leq 10^3\ ) n ( 1n103 )

输出描述:

输出一个整数,代表题目总数 。

题解

对于完整的一周,小红要出 5 × 3 = 15 5 \times 3 = 15 5×3=15 道题,剩余的天数中小红最多出 5 5 5 天题目。

代码如下

n = int(input())
print(n//7*15 + min(n%7, 5)*3)

B 串串香

https://ac.nowcoder.com/acm/contest/99990/B

给定一个长度为 n n n ,仅包含小写字母的字符串 s s s

请你构造出一个非空字符串 t t t ,使得它在 s s s 中作为子串出现的次数最多。

子串是指,从原字符串中连续截取一些字符,得到的新字符串。

输入描述:

第一行有一个整数 n   (   1 ≤ n ≤ 1 0 5   ) n\ (\ 1 \leq n \leq 10^5\ ) n ( 1n105 )

第二行有一个字符串 s s s ,字符串仅包含小写字母。

输出描述:

输出一个字符串,代表构造得到的字符串 t t t

如果有多个字符串符合条件,输出任意一个即可。

题解

不难想到,要找到出现次数最多的子串,子串长度为 1 1 1 时一定能找到一个。

证明如下:

如果有字符串 t 1 t1 t1 满足 ∣ t ∣ > 1 \left | t \right | > 1 t>1 t 1 t1 t1 出现的次数是最多的, 那么 t 1 t1 t1 的子串 t 2 t2 t2 出现的次数 一定和 t 1 t1 t1 相同。

不断细分下去最终一定能得到一个长度为 1 1 1 的子串。

代码如下

from collections import Counter
n = int(input())
s = input()
 
mm = Counter(s)
for k in mm:
    if mm[k] == max(mm.values()):
        print(k)
        break

C.小红的gcd

https://ac.nowcoder.com/acm/contest/99990/C

小红有一个长度为 n n n 的数组,她希望数组元素之和越少越好。

她可以进行任意次操作,每次选择数组中的两个元素 a i a_i ai a j a_j aj ,令 a i = a j = gcd ⁡ ( a i , a j ) a_i=a_j=\gcd(a_i,a_j) ai=aj=gcd(ai,aj)

所有操作结束后,请你输出最小的数组元素之和。

输入描述:

第一行有一个整数 n   (   1 ≤ n ≤ 1 0 5   ) n\ (\ 1 \leq n \leq 10^5\ ) n ( 1n105 )

第二行有 n n n 个整数 a i   (   1 ≤ a i ≤ 1 0 9   ) a_i\ (\ 1 \leq a_i \leq 10^9\ ) ai ( 1ai109 )

输出描述:

第一行有一个整数 n   (   1 ≤ n ≤ 1 0 5   ) n\ (\ 1 \leq n \leq 10^5\ ) n ( 1n105 )

第二行有 n n n 个整数 a i   (   1 ≤ a i ≤ 1 0 9   ) a_i\ (\ 1 \leq a_i \leq 10^9\ ) ai ( 1ai109 )

题解

对于 a , b , c a, b, c a,b,c g c d ( a , b , c ) = g c d ( g c d ( a , b ) , c ) gcd(a, b, c) = gcd(gcd(a, b), c) gcd(a,b,c)=gcd(gcd(a,b),c)

因为次数不限,我们总能把所有元素都变为他们的公共 g c d gcd gcd,和即为 g c d ∗ n gcd * n gcdn

代码如下

from math import gcd 

n = int(input())
a = list(map(int, input().split()))
num = a[0]
for i in range(1, len(a)):
    num = gcd(num, a[i])
    if num == 1:
        break
print(num*n)

D.奇偶调整

本题是假题,略过
本题是假题·

E.幂次进近

https://ac.nowcoder.com/acm/contest/99990/E

给定 t t t 次询问,每次询问给出两个正整数 n n n k k k

请你找到最小的正整数 m m m ,使得 n − m k n-m^k nmk绝对值最小。

输入描述:

第一行有一个整数 t   (   1 ≤ t ≤ 1 0 5   ) t\ (\ 1 \leq t \leq 10^5\ ) t ( 1t105 )

随后 t t t 行,每行两个整数 n , k   (   1 ≤ n , k ≤ 1 0 18   ) n,k\ (\ 1 \leq n,k \leq 10^{18}\ ) n,k ( 1n,k1018 )

输出描述:

输出 t t t 行,每行一个正整数 m m m

题解

k > = 61 k >= 61 k>=61 时, 2 61 = 2305843009213693952 > 2 ∗ 1 0 18 2^{61} = 2305843009213693952 > 2*10^{18} 261=2305843009213693952>21018, 此时答案一定为 1 1 1

k = 1 k = 1 k=1 时,答案为 n n n

1 < k < 61 1 < k < 61 1<k<61时,答案为 ⌊ n 1 k ⌋ k \lfloor n^{\frac{1}{k}} \rfloor^k nk1k ( ⌊ n 1 k ⌋ + 1 ) k ({\lfloor n^{\frac{1}{k}} \rfloor +1})^k (nk1+1)k 中离 n n n 近的那一个。

但是 ⌊ n 1 k + 1 ⌋ k \lfloor n^{\frac{1}{k}} + 1 \rfloor^k nk1+1k 会超过 64 64 64 位整数 ,我们可以把比较的式子做一定的变化
( ⌊ n 1 k ⌋ + 1 ) k − n > = n − ⌊ n 1 k ⌋ k ({\lfloor n^{\frac{1}{k}} \rfloor +1})^k - n >= n - \lfloor n^{\frac{1}{k}} \rfloor^k (nk1+1)kn>=nnk1k
⇔ ( ⌊ n 1 k ⌋ + 1 ) k − 2 ⋅ n + ⌊ n 1 k ⌋ k > = 0 \Leftrightarrow ({\lfloor n^{\frac{1}{k}} \rfloor +1})^k - 2 \cdot n + \lfloor n^{\frac{1}{k}} \rfloor^k >= 0 (nk1+1)k2n+nk1k>=0
⇔ ( 1 + 1 ⌊ n 1 k ⌋ k ) k − 2 ⋅ 1 ⌊ n 1 k ⌋ k + 1 > = 1 \Leftrightarrow ({1 + \frac{1}{\lfloor n^{\frac{1}{k}}\rfloor^k} })^k - 2 \cdot \frac{1}{\lfloor n^{\frac{1}{k}}\rfloor^k} + 1 >= 1 (1+nk1k1)k2nk1k1+1>=1

计算时使用 l o n g    d o u b l e long\, \, double longdouble 来确保精度不会出错

当然还有一种更简单的方法是用 p y t h o n python python

代码如下

from math import floor, ceil
t = int(input())
for _ in range(t):
    n, k = map(int, input().split())
    if k >= 61:
        print(1)
    elif n==1 or k == 1:
        print(n)
    else:
        x = n ** (1.0 / k)
        m1 = floor(x)
        m2 = ceil(x)
        m1 = max(m1, 1)
        if n - m1 ** k <= m2**k - n:
            print(m1)
        else:
            print(m2)
#include <bits/stdc++.h>
using namespace std;
  
typedef long long ll;
  
inline ll read(){
    ll s = 0, w = 1; char ch = getchar();
    while (ch < 48 || ch > 57) { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= 48 && ch <= 57) s = (s << 1) + (s << 3) + (ch ^ 48), ch = getchar();
    return s * w;
}
   
inline void pt(ll x){if(x<0) putchar('-'),x=-x;if(x>9) pt(x/10);putchar(x%10+'0');}  
void print(ll x){pt(x), puts("");}

ll qpow(ll x, ll p) {
        ll res = 1;
        for (; p; p >>= 1, x = x * x)
            if (p & 1) res = res * x;
        return res;
}

void solve(){
    ll n = read(), k = read();
    if (k>= 61){
        print(1);
    }
    else if (k > 1){
        ll m = static_cast<ll>(pow(n, (long double)1.0/k));
        ll mk = qpow(m, k);
        long double n1 = (long double)1.0 * n/mk;
        long double n2 = pow((1+(long double)1.0/m), k);
        if (m == 0){
            print(1);
        }
        else if (n2 - 2*n1 + 1<0){
            print(m+1);
        }
        else{
            print(m);
        }
    }
    else{
        print(n);
    }
}

int main(){
    ll t = read();
    while(t--){
        solve();
    }
}

F.同位序列

定义 f ( x ) f(x) f(x) x x x 在二进制表示下的 1 1 1 的个数。

例如 f ( 7 ) = 3 f(7)=3 f(7)=3 f ( 8 ) = 1 f(8)=1 f(8)=1 f ( 9 ) = 2 f(9)=2 f(9)=2

定义 g ( x ) g(x) g(x) 为 第一个比 x x x 大的数字 y y y ,使得 f ( x ) = f ( y ) f(x)=f(y) f(x)=f(y)

例如 g ( 1 ) = 2 g(1)=2 g(1)=2 g ( 2 ) = 4 g(2)=4 g(2)=4 g ( 3 ) = 5 g(3)=5 g(3)=5

给你一个长度为 n n n 的数组 a a a ,第 i i i 个元素为 a i a_i ai

我们希望从数组 a a a 中挑选出一些元素,构造一个长度为 m m m 的同位序列 b b b

同位序列 满足如下条件:

对于所有的 j ∈ [ 2 , m ] j \in [2,m] j[2,m] ,都有 b j = g ( b j − 1 ) b_j=g(b_{j-1}) bj=g(bj1)

当然,同位序列越长越好,请你最大化其长度 m m m ,然后输出 m m m 和对应的同位序列。

如果有多解,输出任意一个即可。

输入描述:

第一行有一个整数 n   (   1 ≤ n ≤ 1 0 5   ) n\ (\ 1 \leq n \leq 10^5\ ) n ( 1n105 )

第二行有 n n n 个整数 a i   (   1 ≤ a i ≤ 1 0 9   ) a_i\ (\ 1 \leq a_i \leq 10^9\ ) ai ( 1ai109 )

输出描述:

第一行输出一个整数 m m m ,代表 最长的同位序列 的长度。

第二行输出 m m m 个整数,代表同位序列中的元素。

题解

本题的难点其实是如何 O ( l o g n ) O(logn) O(logn) O ( 1 ) O(1) O(1) 复杂度内求出 g ( x ) g(x) g(x)

本质上 g ( x ) g(x) g(x) 其实是 在不改变 1 1 1 0 0 0 的数目下的下一个排列。

注意 g ( x ) > x g(x) > x g(x)>x

这里给出几种求法

def g(x):
   # O(logn)
    f = False
    for i in range(35):
        if x >> i & 1 == 0:
            if f:
                x |= 1 << i
                v = x & ((1 << i) - 1)
                x -= v
                x |= (1 << bin(v).count('1') - 1) - 1
                return x
        else:
            f = True
 
 def g2(x):
  	# O(logn)
    c = x
    c0 = 0
    while (c & 1) == 0 and c != 0:
        c0 += 1
        c >>= 1
    c1 = 0
    while (c & 1) == 1:
        c1 += 1
        c >>= 1
    p = c0 + c1
    x |= 1 << p
    x &= ~((1 << p) - 1)
    x |= (1 << (c1 - 1)) - 1
    return x
    
def g3(x):
    # O(1)
    o=x&-x
    t=x+o
    c=t&-t
    m=((c//o)>>1)-1
    ans=t|m
    return ans

我们预处理出每一个 g ( a i ) g(a_i) g(ai),在从大到小计算以 a i a_i ai为开头的最长序列长度。

g ( a i ) g(a_i) g(ai) 在序列中,则 l ( a i ) l(a_i) l(ai) l ( g ( a i ) ) + 1 l(g(a_i)) + 1 l(g(ai))+1;否则为 1 1 1

我们选取任意一个最长的序列,在还原他即可

代码如下

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

def g(x):# g(x)略去,详见上面的代码

nex = {x: g(x) for x in nums}

maxlen = {}
nums.sort(reverse=True)

for i in nums:
    maxlen[i] = 1 if nex[i] not in maxlen else maxlen[nex[i]] + 1 # 哈希表in是O(1)
  
mx = max(maxlen.values())
for i in maxlen:
    if maxlen[i] == mx:
        ans = [i]
        for _ in range(mx - 1):
            ans.append(nex[ans[-1]])
        print(len(ans))
        print(*ans)
        break
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值