alt

T1:最小字典序

问题描述

时间限制:1.000S 空间限制:256MB

题目描述

小红有一个 01 字符串,她可以进行最多 k 次提作,每次操作可以交换相邻的两个字符,问可以得到的字典序最小的字符串是什么。 输入描述

第一行包含两个整数,n(1 < n < 10^5)和 k(1 < k < 10^9),表示字符串的长度和可以进行的操作次数。

接下来一行一个长度为 n 的 01 字符串。

输出描述

输出一个长度为 n 的字符串,表示字典序最小的字符串。

输入示例

5 2

01010

输出示例

00101

思路

这个题的n的范围是,k的范围是,所以暴力是不可能暴力的。

分析一下规律:

以10101开始分析

k=1:01101

k=2:01011

k=3:00111

k>3:00111

总结起来就是说:从前往后扫,把前面的0尽可能前移

编程过程

Step1:记录前缀和(1的个数)

如 1010101010 ==> 1 1 2 2 3 3 4 4 5 5 我们只需要0对应的位置

前缀和记录的是1的个数,如pre[1] = 1指的是当前的0可以前移1位

pre[5] = 3:当前面两个0前移之后,变成00111 0 1010,此时pre[5]指的是当前的0可以前移3位

Step2:遍历字符串

Case1:`s[idx]=="1"`,idx += 1

Case2:`s[idx]=="0" and pre[idx] > 0`

    if k > pre[idx] : 把0移动至最前面
    else: 把0前移k-pre[idx]位

即:把K拆解成多个前缀之和,如s=1010101010 k=8 pre= [1 1 2 2 3 3 4 4 5 5]

8 = 1 + 2 + 3 + 2

代码

def fun(n,k,s):
    idx = 0
    # 前缀和
    pre =[0]
    for ss in s:
        if ss == "1":
            pre.append(pre[-1]+1)
        else:
            pre.append(pre[-1])
    # 处理超大K值
    _sum = 0
    for i,ss in enumerate(s):
        if ss == "0":
            _sum += pre[i+1]
    if k >= _sum:
        print("0"*s.count("0") + "1"*s.count("1"))
        return
    # 举个例子:1010101010 k=8
    # 记录一下前缀和:pre: 1 1 2 2 3 3 4 4 5 5  
    # 前缀和记录的是1的个数,如pre[1] = 1指的是当前的0可以前移1位
    # pre[5] = 3:当前面两个0前移之后,变成00111 0 1010,此时pre[5]指的是当前的0可以前移3位
     
    # 我们可以观察到0对应的前缀分别为1 2 3 4 5,那么8 = 1 + 2 + 3 + 2
    # 也就是说前三个0前移:0001111010,此时还剩下2次移动,00011 0 11 10
    while k > 0 and idx < n:
        while (idx < n and s[idx] == "1") or pre[idx+1]<=0: idx += 1
        if idx == n: break
        if k >= pre[idx+1]:
            s = "0"+s[:idx] + s[idx+1:]
            k -= pre[idx+1]
            pre[idx + 1] = -1
            if k == 0: break
        else:
            s = s[:idx-k] + "0" +s[idx-k:idx] + s[idx+1:]
            break
 
    print(s)
 
n,k = map(int,input().split(" "))
s = input()
fun(n,k,s)

T2:子序列个数

时间限制:1.000S 空间限制:256MB

题目描述

讨厌鬼有一个长度为 n (1 < n < 10^5)的数组,他想知道这个数组有多少个子序列是一个排列?

子序列的定义: 数组删除若干个元素(也可以不删)后得到的新数组。

排列的定义: 长度为 m 的数组,1 到 m 每个元素都出现过,且恰好出现 1 次。

输入描述

第一行输入一个整数 n。

第二行输入 n 个正整数,a1, a2, ..., an。(1 <= ai <= 10^9)

输出描述

一行一个整数,表示有多少个子序列是一个排列。由于答案过大,请将答案对 10^ 9 + 7 取模后输出

输入示例

6

1 1 5 2 3 4

输出示例

10

提示信息

符合要求的子序列有:{1},{1},{1,2},{1,2},{1,2,3},{1,2,3},{1,2,3,4},{1,2,3,4},{1,5,2,3,4},{1,5,2,3,4}共 10 个

思路

n = 6,nums=1 1 5 2 3 4为例

cnt = {1:2,2:1,3:1,4:1,5:1}

其实就是计数

1 , 1-2, 1-3, 1-4, 1-5 的个数 ==>2, 2*1, 2*1*1, 2*1*1*2, 2*1*1*1*1 也就是 累乘和,所以又是一道前缀和

代码

from collections import Counter

n = int(input())
nums = list(map(int,input().split(" ")))
dic = Counter(nums)
nums = list(set(nums))
nums.sort()

idx = 1
ans = 0
if dic[1] == 0:
    print(0)
else:
    pre = [1]
    for num in nums:
        pre.append((pre[-1]*dic[num])%(10**9+7))
    # print(dic)
    # print(pre)
    while idx <= nums[-1]:
        if idx in dic:
            ans += pre[idx]
            ans = ans %(10**9+7)
            idx+=1
        else:
            break
    print(ans)