506完美数
题目描述
对于给定的数字 a , b ,当整数 n 在十进制下的所有数位都为 a 或 b 时,我们称 n 是“好数”
对于好数 n ,当 n 在十进制下每一位的数字之和也为“好数”时,我们称 n 是一个“完美数”
请你求出有多少 m 位数是“完美数”
输入格式
输入一行三个整数 a , b , m , 含义如题面所示 ( 1 ≤ m ≤ 1 0 6 , 1 ≤ a , b ≤ 9 ) (1≤m≤10^6,1≤a,b≤9) (1≤m≤106,1≤a,b≤9)。
输出格式
输出一行一个整数表示完美数的数量 , 由于答案可能很大 , 请你将答案对 1 0 9 + 7 10^9+7 109+7 取模
样例输入
5 1 5
样例输出
1
样例解释
只有 11111 满足要求
思路
又不太会·,继续参考大佬~
新知识:快速幂、逆元
- 逆元
定义:若有 ( a × b ) % p ≡ 1 (a×b){\%}p≡1 (a×b)%p≡1,我们称b为a的乘法逆元
性质: ( a / b ) % p = a ∗ i n v ( b ) % p (a/b)\%p=a*inv(b)\%p (a/b)%p=a∗inv(b)%p即逆元可以除法转化为乘法 - 快速幂
见代码
代码
#include<bits/stdc++.h>
using namespace std;
const long long mod = 1e9+7;
const int Max = 1e6+1;
int a,b,m;
long long fact[Max]; //fact[i]表示i的阶乘
long long infact[Max]; //infact[i]表示i的阶乘的逆元
bool check(long long x){ //检查是不是完美数
while(x){
if(x%10!=a && x%10!=b) return false;
x/=10;
}
return true;
}
long long pow(long long a,long long b){ //快速求幂
long long p=1;
while(b){
if(b&1) p = p*a%mod;
a=a*a%mod;
b >>= 1;
}
return p;
}
void inpow(){ //求阶乘及阶乘的逆元
fact[0]=1;
for(int i=1; i < Max; i++)
fact[i]=fact[i-1]*i%mod;
infact[Max-1] = pow(fact[Max-1],mod-2);
for(int i=Max-2; i>=0; i--)
infact[i]=infact[i+1]*(i+1)%mod;
}
int main(){
long long ans = 0;
cin >> a >> b >> m;
inpow();
if(a == b){
cout << 0;
return 0;
}
if(a>b) swap(a,b);
for(int i=0; i<=m; i++){
long long sum = i*a+(m-i)*b;
if(check(sum)){
long long i2=min(i,m-i);
ans = (ans + fact[m]*infact[i2]%mod*infact[m-i2]%mod)%mod;
}
}
cout << ans%mod;
return 0;
}
603整除光棍
题目描述
这里所谓的“光棍”,并不是指单身汪啦~ 说的是全部由 1 组成的数字,比如 1、111、111、1111 等。传说任何一个光棍都能被一个不以 5 结尾的奇数整除。比如, 111111 就可以被 13 整除。 现在,你的程序要读入一个整数 x ,这个整数一定是奇数并且不以 5 结尾。然后,经过计算,输出两个数字:第一个数字 s,表示x 乘以 s 是一个光棍,第二个数字n是这个光棍的位数。这样的解当然不是唯一的,题目要求你输出最小的解。
提示:一个显然的办法是逐渐增加光棍的位数,直到可以整除 x 为止。但难点在于,s 可能是个非常大的数 —— 比如,程序输入 31 ,那么就输出 3584229390681和 15 ,因为 31 乘以 3584229390681 的结果是 111111111111111 ,一共 15 个 1。
输入格式
输入在一行中给出一个不以 5 结尾的正奇数 x(x<1000)。
输出格式
在一行中输出相应的最小的 s 和 n,其间以 1 个空格分隔。
样例输入
31
样例输出
3584229390681 15
思路
增加光棍的数量,直到整除x
先找到大于x的最小光棍,除以x取整,余数继续增加光棍,再除x,反复迭代,直到能整除x
代码
#include<bits/stdc++.h>
using namespace std;
int x;
int main(){
cin >> x;
int gl = 1;
int num = 1;
while(gl < x){
gl = gl*10 + 1;
num++;
}
while(1){
if(gl/x==0){
gl=gl*10+1;
num++;
}
cout << gl/x;
gl %= x;
if(gl == 0) break;
}
cout << " " << num;
return 0;
}
604碰撞2
题目描述
在 xy 坐标系中有 N 个人,第 i 个人的位置是 ( X i , Y i ) (X_i,Y_i) (Xi,Yi),并且每个人的位置都不同。
我们有一个由 L 和 R 组成的长为 N 的字符串 S, S i = R S_i= R Si=R 代表第 i 个人面向右, S i = L S_i= L Si=L 代表第 i 个人面向左。
现在所有人开始朝着他们各自面向的方向走,即面向右 x 就增,面向左 x 就减。
例如,当 ( X 1 , Y 1 ) = ( 2 , 3 ) , ( X 2 , Y 2 ) = ( 1 , 1 ) , ( X 3 , Y 3 ) = ( 4 , 1 ) , S = R R L (X_1,Y_1)=(2,3),(X_2,Y_2)=(1,1),(X_3,Y_3)=(4,1),S= RRL (X1,Y1)=(2,3),(X2,Y2)=(1,1),(X3,Y3)=(4,1),S=RRL 时,人们的移动如图。
我们把两个人对向行走到一个位置称为一次碰撞。请问如果人们可以无限走下去,会有人产生碰撞吗?
输入格式
第一行一个整数 N;
接下来 N 行,每行两个整数 X i X_i Xi 和 Y i Y_i Yi,表示第 i 个人的位置;
最后一行是一个由 L 和 R 组成的长为 N 的字符串 S。
输出格式
如果会有碰撞,输出 Yes,否则输出 No。
样例输入 1
3
2 3
1 1
4 1
RRL
样例输出 1
Yes
样例输入 2
2
1 1
2 1
RR
样例输出 2
No
样例输入 3
10
1 3
1 4
0 0
0 2
0 4
3 1
2 4
4 2
4 4
3 3
RLRRRLRLRR
样例输出 3
Yes
数据规模
所有数据保证 2 ≤ N ≤ 2 × 1 0 5 , 0 ≤ X i ≤ 1 0 9 , 0 ≤ Y i ≤ 1 0 9 2≤N≤2×10^5,0≤X_i≤10^9,0≤Y_i≤10^9 2≤N≤2×105,0≤Xi≤109,0≤Yi≤109。
思路
按y从小到大排好序,y相同按x从小到大排序,如果一行中靠左边的向右,靠右边的向左,那么必相撞
思路
#include<bits/stdc++.h>
using namespace std;
const int Max = 2e5+5;
int n;
string s;
struct posi{
long long x, y;
char f;
bool operator<(posi a){
return y < a.y || (y==a.y && x < a.x);
}
}p[Max];
int main(){
cin >> n;
for(int i = 1; i <= n; i++){
cin >> p[i].x >> p[i].y;
}
cin >> s;
for(long long i = 0; i < s.length(); i++){
p[i+1].f = s[i];
}
sort(p+1, p+1+n);
int flag = 0;
long long now = p[1].y;
for (int i = 1; i <= n; i++){
if(p[i].y != now){
now = p[i].y;
flag = 0;
}
if(p[i].f == 'R'){
flag = 1;
}
if(p[i].f == 'L' && flag == 1){
cout << "Yes" << endl;
return 0;
}
}
cout << "No" << endl;
}
605优美!最长上升子序列
题目描述
每组将给定一个数组。派派希望从中选择一个递增的子序列,越长越好。
但派派认为,这样选出来的子序列依然不够「优美」,形式化的讲,派派希望选择的下标(从 1 开始)需要满足
i
1
∣
i
2
∣
i
3
∣
⋯
∣
i
k
i_1∣i_2∣i_3∣⋯∣i_k
i1∣i2∣i3∣⋯∣ik
其中 a|b 表示整除, 即 a 是 b 的约数。
请你帮助派派完成任务吧!
注:子序列的含义不再赘述。
输入格式
第一行一个整数 T,表示接下来有 T 组数据。
每组数据包含两行,第一行包含一个整数 N。
随后一行,包含 N 个整数,表示原数组 {A}。
输出格式
对于每组数据,输出一行,包含一个数,表示能选出的「优美」的最长上升子序列长度。
数据规模
1
≤
T
≤
100
1≤T≤100
1≤T≤100
1
≤
N
≤
1
0
6
1≤N≤10^6
1≤N≤106,但保证
∑
i
=
1
T
N
i
≤
1
0
6
∑_{i=1}^TN_i≤10^6
∑i=1TNi≤106
1
≤
A
i
≤
1
0
9
1≤A_i≤10^9
1≤Ai≤109
样例输入
4
4
1 4 6 7
2
2 2
10
1 2 3 4 5 6 7 8 9 10
10
10 9 8 6 5 2 3 1 2 1
样例输出
3
1
4
1
解释:
对于第一组数据,能选择的「优美」最长上升子序列为 {A1,A2,A4}={1,4,7}。
对于第三组数组,选择 {A1,A2,A4,A8}={1,2,4,8}。
对于第四组数据,可选择的「优美」最长上升子序列长度为 1。
思路
动态规划,
d
p
[
i
]
=
m
a
x
{
d
p
[
j
]
+
1
,
d
p
[
i
]
}
,
j
=
1
,
2...
i
−
1
dp[i]=max\{dp[j]+1,dp[i]\}, j=1,2...i-1
dp[i]=max{dp[j]+1,dp[i]},j=1,2...i−1
只不过要注意间隔不同
代码
#include<bits/stdc++.h>
using namespace std;
const int Max = 1e6+5;
int t;
long long a[Max];
int dp[Max];
int main(){
cin >> t;
for(int i = 1; i <= t; i++){
int n;
cin >> n;
for(int j = 1; j <= n; j++) cin >> a[j];
for(int j = 1; j <= n; j++) dp[j]=1;
for(int j = 1; j <= n; j++){
for(int k = 2*j; k <= n; k+=j){
if(a[j] < a[k]) dp[k] = max (dp[j]+1, dp[k]);
}
}
int ans = 0;
for(int j = 1; j <= n; j++){
ans = max(dp[j], ans);
}
cout << ans <<endl;
}
}
606 巨大的牛棚
题目描述
农夫约翰想要在他的正方形农场上建造一座正方形大牛棚。他讨厌在他的农场中砍树,想找一个能够让他在空旷无树的地方修建牛棚的地方。我们假定,他的农场划分成 n * n的方格。输入数据中包括有树的方格的列表。你的任务是计算并输出,在他的农场中,不需要砍树却能够修建的最大正方形牛棚。牛棚的边必须和水平轴或者垂直轴平行。 考虑下面的方格,它表示农夫约翰的农场,‘.'表示没有树的方格,‘#'表示有树的方格
........
.#...#..
........
........
........
..#.....
........
........
那么最大的牛棚是5*5的。
输入描述
第一行输入一个正整数 n(1≤n≤1000)代表农场的大小,一个正整数T(1≤T≤n∗n), 接下来 T 行,每行2个整数,代表有树的格子的横纵坐标,保证任意两个树格子不相同
输出描述
输出一个正整数代表牛棚的最大边长
样例输入
8 3
2 2
2 6
6 3
样例输出
5
思路
暴力解法:
求一个二位前缀和,然后枚举最大边长就可以
代码
#include <bits/stdc++.h>
using namespace std;
const int Max = 1e3+5;
int n,t;
int ans;
int sum[Max][Max],mapp[Max][Max];
int main(){
cin >> n >> t;
for(int i = 1; i <= n; i++){
for(int j = 1; j <=n ; j++){
mapp[i][j]=1;
sum[i][j]=0;
}
}
for(int i = 1; i <= t; i++){
int x,y;
cin >> x >> y;
mapp[x][y]=0;
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
sum[i][j] = sum[i-1][j] + sum[i][j-1] - sum[i-1][j-1] + mapp[i][j];
}
}
for(int i = 1; i <= n; i++){
for(int j = 1; j <= n; j++){
while(i-ans>=0 && j-ans>=0 && sum[i][j]-sum[i-ans][j]-sum[i][j-ans]+sum[i-ans][j-ans] == ans*ans)
ans++;
}
}
cout << ans-1 << endl;
return 0;
}
607 高利贷
题目描述
19岁的大学生小 L 家里情况不好,每月固定生活费不足以买一部苹果手机。当他得知有贷款机构可以贷小额贷款并且可以分期等额还款,每月只需要还几百块时,在虚荣心的驱使下他开始自己的贷款之旅。
贷款机构声称利率按月累计,贷款人在贷款期间每月偿还固定的分期付款金额。
给出小 L 贷款的原值为 n,分期付款金额 m 和分期付款还清贷款所需的总月数 k,求该贷款的月利率 p。
输入格式
三个用空格隔开的正整数 n,m,k,含义如上述所示。
输出格式
一个实数 p,表示该贷款的月利率(用小数表示),和标准输出绝对值不超过10−6即可。
数据范围
1
≤
n
,
m
≤
1
0
6
,
1
≤
k
≤
300
1≤n,m≤10^6,1≤k≤300
1≤n,m≤106,1≤k≤300
0
≤
p
≤
5
0≤p≤5
0≤p≤5,
n
≤
m
×
k
n≤m×k
n≤m×k
输入样例1
1000 1200 1
输出样例1
0.200000
输入样例2
1000 100 12
输出样例2
0.029229
样例解释
对于第一组样例,小 L 贷款了 1000 元,并在一个月后支付 1200 元还清了贷款。因此计算月利率 p 的公式为 1000×(1+p)=1200 或 12001+p=1000,求出 p=0.200000。
对于第二组样例,小 L 贷款了 1000 元,并以每月支付 100 元的方式在 12 个月后还清了贷款。由于月利率的存在,他第 k 个月偿还的金额只相当于 100(1+p)k 元的初始金额,且这12个月共偿还初始金额1000元,求出 p=0.029229。
思路
此题用到幂运算,为方便我用python完成
数学建模课程曾学到过利率的计算
每月还款额
p
=
a
0
r
(
1
+
r
)
n
/
(
(
1
+
r
)
n
−
1
)
p=a_0r(1+r)^n/((1+r)^n-1)
p=a0r(1+r)n/((1+r)n−1)
其中
a
0
a_0
a0为借款额,r为利率,n为还款总月份
由于不能反解出r,我们使用二分即可找到利率的值
代码
# -*- coding: utf-8 -*-
"""
Created on Wed Apr 12 21:15:15 2023
@author: lenovo
"""
def judge(r,a,p,k):
tmp = a*r*((1+r)**k)/((1+r)**k-1)
if tmp>p:
return 1
elif tmp<p:
return 0
else:
return 2
if __name__ == '__main__':
#a0 = eval(input())
#p = eval(input())
#k = eval(input())
a0,p,k=map(int,input().split())
l,r = 0,2
while l<r:
mid = (l+r)/2
if abs(r-l) <= 1e-6:
ans = l
break
if judge(mid,a0,p,k) == 1:
r = mid
elif judge(mid,a0,p,k) == 0:
l = mid
else:
ans = mid
break
print("%0.6f" %ans)
701 背包
题目描述
cc有一个背包,背包的体积为w,有n个物品,每一个物品的体积为
a
i
a_i
ai
cc希望将其中的一些物品放入他的背包中,他希望这些物品的体积之和至少是背包体积的一半,并且不超过背包的体积,即
⌈
w
/
2
⌉
≤
s
u
m
≤
w
⌈w/2⌉≤sum≤w
⌈w/2⌉≤sum≤w
请你帮cc判断这些物品中有没有符合条件的物品组合,如果有输出"YES", 没有输出"NO"
cc至少会拿一个物品
输入格式
第一行给出测试样例个数T
对于每一个样例:
第一行给出一个n和一个w,n个物品,背包的总体积是w
第二行给出一个序列
a
1
,
.
.
.
,
a
n
a_1,...,a_n
a1,...,an,代表每一个物品的体积
输出格式
如果有请输出"YES", 没有输出"NO"
数据范围
1
≤
t
≤
1
e
4
1≤t≤1e4
1≤t≤1e4
1
≤
∑
n
≤
2
e
5
,
1
≤
w
≤
1
e
18
1≤∑n≤2e5,1≤w≤1e18
1≤∑n≤2e5,1≤w≤1e18
0
≤
w
i
≤
1
e
9
0≤w_i≤1e9
0≤wi≤1e9
样例输入1
3
1 3
3
6 2
19 8 19 69 9 4
7 12
1 1 1 17 1 1 1
样例输出
YES
NO
YES
思路
如果这个物品的大小正好处于一半与最大之间,直接得出答案,输出YES
而对于体积小于背包体积一半的物品,将体积累加,判断总体积是否为一半与最大之间
代码
#include<bits/stdc++.h>
using namespace std;
int t;
int n;
long long w;
long long a;
int main(){
cin >> t;
for(int i = 1; i <= t; i++){
cin >> n >> w;
vector<long long> aa;
int flag = 0;
for(int j = 1; j <= n; j++){
cin >> a;
if(a < ceil(w*1.0/2)) aa.push_back(a);
else{
if(a >= ceil(w*1.0/2) && a <= w && flag == 0){
cout << "YES" << endl;
flag = 1;
continue;
}
}
}
if(flag == 1) continue;
long long sum = 0;
//sort(aa,aa+aa.size());
for(int j = 0; j < aa.size(); j++){
sum += aa[j];
if(sum >= ceil(w*1.0/2) && sum <= w){
flag = 1;
//cout << "YES" << endl;
break;
}
if (sum > w){
//cout << "NO" << endl;
break;
}
}
if(flag == 1) cout << "YES" << endl;
else cout << "NO" << endl;
}
return 0;
}
702 三回文序列
题目描述
给定一个长度为n的序列a。
我们定义三回文序列是形如
的序列,例如:[1,1,1,2,2,1,1,1]是一个三回文序列,而[1,1,1,2,2,2,3,3,3],[1,1,2,2,1]都不是三回文序列。
现在,希望你找到序列a中最长的三回文序列的长度。
注意,k1,k2可以为0
输入格式
第一行给出一个数字T,表示T组测试用例
对于每一个测试用例
第一行给出序列的长度n
第二行给出序列
a
1
,
a
2
,
a
3
.
.
.
a
n
a_1,a_2,a_3...a_n
a1,a2,a3...an
输出格式
对于每一个测试用例,输出最长的三回文序列的长度。
数据范围
1
≤
t
≤
2000
1≤t≤2000
1≤t≤2000
1
≤
∑
n
≤
2
e
5
,
1
≤
a
i
≤
26
1≤∑n≤2e5,1≤a_i≤26
1≤∑n≤2e5,1≤ai≤26
样例输入
6
8
1 1 2 2 3 2 1 1
3
1 3 3
4
1 10 10 1
1
26
2
2 1
3
1 1 1
样例输出
7
2
4
1
1
3
思路
没思路呀,那就只能做搬运工了
遍历一遍数组,记录每个元素的出现次数,然后枚举所有的数作为a。每次枚举把两边的a的个数都算出来,再枚举中间的b的个数,取总和最大值。
先开始参考大佬文章,将change函数写到main中,运行结果总不对,发现每次循环不能把统计好的数目给改了,因为之后会用,故写成函数调用才能不改数组,且数组不能定义为全局的
代码
#include<bits/stdc++.h>
using namespace std;
int t,n;
int change(vector<int>v, int ans, vector<int>nums){
int l=0, r=n-1, cnt=0, res=nums[ans];
while(l<r){
while(l<r && v[l] != ans){
nums[v[l]]--;
l++;
}
while(l<r && v[r] != ans){
nums[v[r]]--;
r--;
}
cnt += min(nums[ans], 2);
nums[ans] -= 2;
l++, r--;
for(int i = 1; i <= 26; i++)res = max(res, cnt + nums[i]);
}
return res;
}
int main(){
cin >> t;
for(int k = 1; k <= t; k++){
cin >> n;
vector<int>v(n),nums(27);
for(int i = 0; i < n; i++){
cin >> v[i];
nums[v[i]]++;
}
int res = 0;
for(int i = 1; i <= 26; i++){
if (nums[i] != 0)
res = max(res, change(v, i,nums));
}
cout << res << endl;
}
}
703 简单的异或问题
题目描述
有一组整数 0 , 1 , 2 , … , 2 m − 1 {0,1,2,…,2^m−1} 0,1,2,…,2m−1, 请从中选出 k 个数,使得这 k 个数的异或和为 n, 请输出最大的满足条件的 k。
输入格式
两个数 n 和 m, 其中 0 ≤ n ≤ 2 m − 1 , 1 ≤ m ≤ 60 0≤n≤2^m−1,1≤m≤60 0≤n≤2m−1,1≤m≤60。
输出格式
输出最大的满足条件的 k。
样例输入
2 2
样例输出
3
样例解释
对于样例,我们可以选择 {0,1,3}。
思路
首先要明白任何从0到
2
m
−
1
2^m−1
2m−1的全部遍历异或值为0
拿0到7举例:0与7,1与6,2与5,3与4异或,会发现它们的异或值全为7,即为
2
m
−
1
2^m−1
2m−1,它们一定能凑出偶数个两两组成得数对,只有m=1时不能凑出。
再看这道题,拿0到7举例
假设我想要得到3,那么一共会有4个数对,其中的两个我们可以舍去,因为它们异或为0,假如现在剩下2与5,3 与4,第一个数对得值是7,由公式
a
b
=
c
等价于
a
c
=
b
a^b=c 等价于 a^c=b
ab=c等价于ac=b 知3与4得7,就是7^4=3,那么现在就会明白当全部个数大于等于4时,只要从全部数对中取掉一个数就能得到我们想要的数。然后在对个数不足4时进行特判即可。
运用到本题就是m个数对
代码
#include <bits/stdc++.h>
using namespace std;
long long n,m;
int main(){
cin >> n >> m;
long long ans = 1;
ans <<= m;
if(n==0 && m==1) cout << "1" << endl;
else if(n==0) cout << ans << endl;
else if(n==1 && m==1) cout << "2" << endl;
else cout << ans-1 << endl;
}
704 子串的循环挪动
题目描述
给出一个字符串 s,你需要执行 m 个任务。每个任务给出两个下标 l i , r i l_i,r_i li,ri 和一个整数 k i k_i ki(字符串的下标从 1 开始),表示你需要循环挪动 s 的子串 s [ l i . . . r i ] s[l_i...r_i] s[li...ri] k i k_i ki 次。请从前到后依次执行给出的每个任务。
字符串的循环挪动操作:将最后一个字符移到第一个字符的位置,并且将其他所有字符向右移一个位置。
比如:如果字符串 s 是 abacaba,一个任务为 l 1 = 3 , r 1 = 6 , k 1 = 1 l_1=3,r_1=6,k_1=1 l1=3,r1=6,k1=1,那么答案为 abbacaa。接下来一个任务为 l 2 = 1 , r 2 = 4 , k 2 = 2 l_2=1,r_2=4,k_2=2 l2=1,r2=4,k2=2,那么我们会得到 baabcaa。
输入格式
第一行一个字符串 s,该字符串只包含小写英文字符。
第二行一个整数 m,表示任务个数。
接下来 m 行每行有三个整数 l i , r i 和 k i l_i,r_i 和 k_i li,ri和ki。
输出格式
输出执行了 m 个任务后的最终的字符串 s。
样例输入
abacaba
2
3 6 1
1 4 2
样例输出
baabcaa
数据规模
对于所有数据保证, 1 ≤ ∣ s ∣ ≤ 10000 1≤|s|≤10000 1≤∣s∣≤10000(|s| 表示字符串 s 的长度), 1 ≤ m ≤ 300 , 1 ≤ l i ≤ r i ≤ ∣ s ∣ , 1 ≤ k i ≤ 1000000 1≤m≤300,1≤l_i≤r_i≤|s|,1≤k_i≤1000000 1≤m≤300,1≤li≤ri≤∣s∣,1≤ki≤1000000。
思路
模拟
代码
#include<bits/stdc++.h>
using namespace std;
string s;
int m;
int l,r,k;
int main(){
cin >> s;
cin >> m;
for(int t = 1; t <= m; t++){
cin >> l >> r >> k;
string sub = s.substr(l-1,r-l+1);
k %= sub.size();
string tmp;
for(int i = sub.size()-k+1; i <= sub.size(); i++) tmp += sub[i-1];
for(int i = 1; i <= sub.size()-k; i++) tmp += sub[i-1];
//cout << tmp << "&&";
int num = 0;
for(int i = l-1; i <= r-1; i++){
s[i] = tmp[num++];
}
}
cout << s << endl;
}