第一题:支付宝活动
在线测评链接:http://121.196.235.151/p/P1137
题目描述
众所周知,在一些消费支付的场合中,往往有“支付宝九五折”的优惠。这天薯条哥来到了超市购买物品,一共有 n n n种物品,每种物品只能购买一个,但有的物品支持优惠活动,有的并不支持。恰好本超市的结账是有“支付宝九五折”优惠的,薯条哥的支付宝余额还剩 k k k元,他想知道他仅使用支付宝进行支付的话最多能买几件物品?
输入描述
输入包含三行。
第一行两个正整数
n
,
k
(
≤
n
≤
1
0
5
)
,
(
1
≤
k
≤
1
0
9
)
n,k(\le n\le 10^5),(1\le k\le 10^9)
n,k(≤n≤105),(1≤k≤109)
第二行包含
n
n
n个正整数
a
i
(
1
≤
a
i
≤
1
0
4
)
a_i(1\le a_i\le 10^4)
ai(1≤ai≤104)表示每个物品的价格。
第三行一个长度为 n n n的只含有0和1的字符串,表示每个物品是否支持优惠。(如果是1代表第 i i i个物品支持优惠,否则不支持。)
输出描述
输出一行一个整数表示答案
样例
输入
3 975
1000 500 500
0 0 1
输出
2
思路:贪心+排序
首先,我们可以计算打折商品打折后的价格,计算好之后,将所有商品按照价格从小到大排序,然后枚举即可,直到枚举到钱不够的情况,停止枚举,输出答案。
注意:本题需要使用double
存储,否则会丢失精度导致计算结果不正确
C++
#include <bits/stdc++.h>
using namespace std;
using namespace std;
const int N=1e5+10;
int n;
double a[N],k;
string s;
int main() {
cin>>n>>k;
for(int i=0;i<n;i++)cin>>a[i];
cin>>s;
for(int i=0;i<n;i++){
if(s[i]=='1'){
a[i]*=0.95; //计算打折后的价格
}
}
sort(a,a+n);
int ans=0;
double sum=0;
for(int i=0;i<n;++i){
sum+=a[i];
if(sum<=k) ans++;
else break;
}
cout<<ans<<endl;
return 0;
}
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
double k = scanner.nextDouble();
double[] a = new double[n];
for (int i = 0; i < n; i++) {
a[i] = scanner.nextDouble();
}
String s = scanner.next();
for (int i = 0; i < n; i++) {
if (s.charAt(i) == '1') {
a[i] *= 0.95; // 计算打折后的价格
}
}
Arrays.sort(a);
int ans = 0;
double sum = 0;
for (int i = 0; i < n; ++i) {
sum += a[i];
if (sum <= k) ans++;
else break;
}
System.out.println(ans);
}
}
Python
n, k = map(int, input().split())
a = list(map(float, input().split()))
s = input()
for i in range(n):
if s[i] == '1':
a[i] *= 0.95
a.sort()
ans = 0
sum = 0
for price in a:
sum += price
if sum <= k:
ans += 1
else:
break
print(ans)
第二题:元辅音方案数
在线测评链接:http://121.196.235.151/p/P1138
题目描述
ak机定义一个字符串的权值是:字符串辅音数量和元音数量的差的绝对值。
例如,“arcaea"的权值是 2,因为有4个元音,2 个辅音权值为
∣
4
−
2
∣
|4-2|
∣4−2∣=2.现在ak机拿到了一个字符串,她想把这个字符串切成两个非空字符串,需要满足两个字符串的权值相等。ak机想知道,有多少种不同的切割方式?我们定义,元音有"aeiou"这五种,其余字母均为辅音,
输入描述
一个仅包含小写字母的字符串,长度不超过 2 × 1 0 5 2\times 10^5 2×105
输出描述
切割方案数。
样例
输入
arcaea
输出
2
思路:前缀和
我们可以使用前缀和数组 p r e 1 , p r e 2 pre1,pre2 pre1,pre2分别统计前 i i i个字符中的元音数量和辅音数量
当我们枚举到位置 i i i时,我们可以根据前缀和数组计算出左边子串的元音数量和辅音数量 c 1 , c 2 c1,c2 c1,c2,
其中 c 1 = p r e 1 [ i ] − p r e 1 [ 0 ] , c 2 = p r e 2 [ i ] − p r e 2 [ 0 ] c1=pre1[i]-pre1[0],c2=pre2[i]-pre2[0] c1=pre1[i]−pre1[0],c2=pre2[i]−pre2[0]
然后再拿总的元音数量和辅音数量计算出右半边对应的数量 c 3 , c 4 c3,c4 c3,c4
如果有 ∣ c 1 − c 2 ∣ = ∣ c 3 − c 4 ∣ |c1-c2|=|c3-c4| ∣c1−c2∣=∣c3−c4∣,则答案+1。
本题我们可以一边枚举一边计算,可以用 c 1 , c 2 c1,c2 c1,c2这两个变量来代替前缀和数组,有点类似前后缀分解的思想。
C++
#include<bits/stdc++.h>
using namespace std;
const int N=2E5+10;
string s;
int w[N],S[N];
int main(){
cin>>s;
int n=s.size();
set<char>st={'a','e','i','o','u'};
int cnt1=0,cnt2=0;
for(int i=0;i<n;i++){
if(st.count(s[i])){
cnt1++;
}
else{
cnt2++;
}
}
int c1=0,c2=0,res=0;
for(int i=0;i<n;i++){
if(st.count(s[i])){
c1++;
}
else{
c2++;
}
int c3=cnt1-c1,c4=cnt2-c2;
if(abs(c1-c2)==abs(c3-c4)){
res++;
}
}
cout<<res<<endl;
return 0;
}
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
String s = scanner.next();
int n = s.length();
Set<Character> st = new HashSet<>(Arrays.asList('a', 'e', 'i', 'o', 'u'));
int cnt1 = 0, cnt2 = 0;
for (int i = 0; i < n; i++) {
if (st.contains(s.charAt(i))) {
cnt1++;
} else {
cnt2++;
}
}
int c1 = 0, c2 = 0, res = 0;
for (int i = 0; i < n; i++) {
if (st.contains(s.charAt(i))) {
c1++;
} else {
c2++;
}
int c3 = cnt1 - c1, c4 = cnt2 - c2;
if (Math.abs(c1 - c2) == Math.abs(c3 - c4)) {
res++;
}
}
System.out.println(res);
}
}
Python
s = input()
n = len(s)
st = {'a', 'e', 'i', 'o', 'u'}
cnt1, cnt2 = 0, 0
for char in s:
if char in st:
cnt1 += 1
else:
cnt2 += 1
c1, c2, res = 0, 0, 0
for char in s:
if char in st:
c1 += 1
else:
c2 += 1
c3, c4 = cnt1 - c1, cnt2 - c2
if abs(c1 - c2) == abs(c3 - c4):
res += 1
print(res)
第三题:变化数组
在线测评链接:http://121.196.235.151/p/P1139
题目描述
薯条哥有一个长度为 n n n 的数组 a a a。他想要使得所有 a i a_i ai 的最大公因子是一个素数。即 g c d ( a 1 , a 2 . . . , a n ) gcd(a_1,a_2...,a_n) gcd(a1,a2...,an)是一个素数。他可以对数组进行任意次操作。
具体的:每次操作,他会选择
i
,
j
i,j
i,j两个下标,同时执行:
a
i
=
a
i
+
2
,
a
j
=
a
j
−
2
a_i= a_i+2,a_j=a_j-2
ai=ai+2,aj=aj−2
请问他是否有可能在任意次操作内将数组变成符合要求的。如果可以,请输出所有可能的最大公因数。
注意,这里要保证 a j a_j aj在操作后仍然是正数,即不能选择 a j ≤ 2 a_j\le 2 aj≤2
输入描述
输入包含两行。
第一行一个正整数 n ( 2 ≤ n ≤ 2 × 1 0 5 ) n(2 ≤n≤2\times 10^5) n(2≤n≤2×105)表示数组长度。
第二行 n n n个正整数 a i ( 1 ≤ a i ≤ 1 0 6 ) a_i(1\le a_i\le 10^6) ai(1≤ai≤106)表示这个数组
输出描述
输入包含一行或两行。
如果可以输出“YES”否则输出“NO”(不含双引号)
如果答案为“YES”,则第二行按照升序输出所有可行的数组 gcd.
样例1
输入
4
1 3 5 9
输出
YES
3
说明
可以选择一次 i = 1 , j = 3 i= 1,j=3 i=1,j=3,这样一来数组变成:
[ 3 , 3 , 3 , 9 ] [3,3,3,9] [3,3,3,9], g c d = 3 。 gcd =3。 gcd=3。
可以证明只有 3这一个答案
样例2
输入
4
2 2 2 2
输出
YES
2
思路:数论+分类讨论
首先观察可知,操作有两个特点
- 不会改变元素的奇偶性,奇数操作完了还是奇数,偶数亦然
- 不会改变数组的元素总和,总和 s u m sum sum是一个固定值
那么我们首先考虑一种极端情况:数组所有元素都是偶数,那么一定有且仅有一个公素数:2,不会存在其他素数了,因此直接输出2即可
本题有一个重要结论,任何奇数/偶数,都可以通过+2、-2来变成任意一个奇数/偶数。
对于数组中又有奇数,又有偶数的情况,我们可以枚举总和 s u m sum sum的素因子(素因子一定是奇数,唯一的偶数素因子就是2,2不可能满足条件)
我们可以枚举所有素因子 x x x,对于枚举到的素因子 x x x,对于数组中的所有奇数,其值至少要等于 x x x,或者就等于 2 x , 3 x , . . . 2x,3x,... 2x,3x,...
对于数组中的所有偶数,其值至少要等于 2 x 2x 2x,或者就等于 4 x , 6 x , . . . 4x,6x,... 4x,6x,...
因此一定要满足下面三个条件
条件1: x x x是 s u m sum sum的因子
条件2: x × ( n + c n t ) ≤ s u m x\times (n+cnt)\le sum x×(n+cnt)≤sum,其中 c n t cnt cnt为数组中偶数的数量(因为偶数至少要为 2 x 2x 2x)
条件3: x > 2 x>2 x>2,因为数组里面有奇数,因此 x x x一定是奇素数
按照上述方式统计素因子即可
C++
#include <bits/stdc++.h>
using namespace std;
const int N=2e5+10;
int a[N],n;
long long sum;
int main() {
cin>>n;
int cnt=0; //记录数组中偶数的个数
for(int i=0;i<n;i++){
cin>>a[i];
sum+=a[i];
if(a[i]%2==0){
cnt++;
}
}
if(cnt==n){ //如果数组中所有的数字都是偶数,有且仅有一个公共素因子:2
puts("YES");
puts("2");
return 0;
}
vector<int>res; //记录所有合法的素因子
long long total=sum;
for(long long i=2;i*(n+cnt)<=total;i++){
if(sum%i==0&&i!=2){
res.push_back(i);
}
while(sum%i==0){ //去除sum中所有i的因子,类似质因数分解的思想
sum/=i;
}
}
if(!res.size())puts("NO");
else{
puts("YES");
for(int &x:res){
cout<<x<<" ";
}
}
return 0;
}
Java
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
int n = scanner.nextInt();
int[] a = new int[n];
long sum = 0;
int cnt = 0; // 记录数组中偶数的个数
for(int i = 0; i < n; i++){
a[i] = scanner.nextInt();
sum += a[i];
if(a[i] % 2 == 0){
cnt++;
}
}
if(cnt == n){ // 如果数组中所有的数字都是偶数,有且仅有一个公共素因子:2
System.out.println("YES");
System.out.println("2");
return;
}
List<Integer> res = new ArrayList<>(); // 记录所有合法的素因子
long total = sum;
for(long i = 2; i * (n + cnt) <= total; i++){
if(sum % i == 0 && i != 2){
res.add((int)i);
}
while(sum % i == 0){ // 去除sum中所有i的因子,类似质因数分解的思想
sum /= i;
}
}
if(res.isEmpty()){
System.out.println("NO");
} else {
System.out.println("YES");
for(int x : res){
System.out.print(x + " ");
}
}
}
}
Python
n = int(input())
a = list(map(int, input().split()))
sum = 0
cnt = 0 # 记录数组中偶数的个数
for i in range(n):
sum += a[i]
if a[i] % 2 == 0:
cnt += 1
if cnt == n: # 如果数组中所有的数字都是偶数,有且仅有一个公共素因子:2
print("YES")
print("2")
else:
res = [] # 记录所有合法的素因子
total = sum
i = 2
while i * (n + cnt) <= total:
if sum % i == 0 and i != 2:
res.append(i)
while sum % i == 0: # 去除sum中所有i的因子,类似质因数分解的思想
sum /= i
i += 1
if not res:
print("NO")
else:
print("YES")
print(" ".join(map(str, res)))