题目描述
在一个夜黑风高的晚上,牛哥哥吃完心爱的烤串串之后,独自一人漫步在南亭的路上。由于吃的太饱了,牛哥哥决定想一道新生赛的题考(折)验(磨)一下新生,通过脑部的思想风暴促进肚子的消化。
突然间,有两块石头碰瓷了牛哥哥的脚,他气急败坏地捡起来,想要让它们消失在远方。但牛哥哥灵机一动,想要用这两块石头出道题目,以此来显示自己思维之强大。牛哥哥很喜欢竞赛,因为可以一直与别人竞争,因此他也想让两块石头一决高下!
假设两块石头的重量分别为a、b,如果只是简单地比较两者的重量,未免也太简单了。因此,牛哥哥决定计算a!和b!的大小,但因为一个数的阶乘十分之巨大,牛哥哥决定将他们阶乘的结果模一个数再进行比较。此时牛哥哥心中浮现出一个奇妙的模数------999068070,这可是一个很巧妙的模数哦,他心里想。
至此牛哥哥已经完善了他的比较过程,即已知a,b,比较(a!)%999068070和(b!)%999068070的大小,大者则胜出,如相等则打平
作为新手的你们,需要接受牛哥哥的考验,完成比较。
输入描述:
第一行输入两个两个正整数a,b(1≤a,b≤109).
输出描述:
如果重量为a的石头胜出,则输出"a is the winner!"
如果重量为b的石头胜出,则输出"b is the winner!"
如果两者打平,则输出"There is no winner!"
注意:不需要输出""
示例一:
输入:
2 1
输出:
a is the winner!
示例二:
输入:
1 2
输出 :
b is the winner!
示例三:
输入:
2 2
输出:
There is no winner!
示例四:
输入:
23 22
输出:
b is the winner!
备注:
N的阶乘(记作 N!)是指从1到N(包括1和N)的所有整数的乘积。 取余运算符 (%),一个表达式的值除以另一个表达式的值,返回求余结果。求余是一种数学计算方法,指一个数除以另一个数,不够除的部分就是余数,就是求余的结果。
例如:5%3=2
(22!)%999068070=785674890
(23!)%999068070=87297210
评价:
要不是看了答案解析,我是万万想不到会是这么个情况啊!虽然我后面想到了这个模有奥秘之处,但我也想不到这个啊:99068070=2×3×5×7×11×13×17×19×103。
因此当n大于等于103时,答案为0;n小于103时,暴力计算即可。
下面是代码(我不服!):
#include
using namespace std;
typedef long long ll;
const int mod = 999068070;
int f(ll n)
{
if(n<=1) return 1;
else return n*f(n-1)%mod;
}
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll a,b;
cin >> a >> b;
if(a>=103&&b>=103) {cout << "There is no winner!";return 0;}
else if(a>=103&&b<103) {cout << "b is the winner!";return 0;}
else if(a<103&&b>=103) {cout << "a is the winner!";return 0;}
else
{
ll x = f(a);
ll y = f(b);
if(x>y) {cout << "a is the winner!";return 0;}
else if(x<y) {cout << "b is the winner!";return 0;}
else {cout << "There is no winner!";return 0;}
}
return 0;
}
不服?那咱再来讲讲:
合数分解:
由数学基本定理可知:任何一个大于1的非素数整数(即合数)都可以唯一分解成若干个素数的乘积。
由此,则可以判定一定存在某个数(或某个位置吧),使得之后的阶乘对mod取模都为0(由乘法的模运算可知),此时可以去猜。
下面是另一种写法:
#include
using namespace std;
typedef long long ll;
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
const ll mod = 999068070;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll a,b;
cin >> a >> b;
auto solve = [](int x)
{
if(x>=200) return 0ll;
ll r = 1;
rep(i,1,x) r = r*i%mod;
return r;
};
ll x = solve(a),y = solve(b);
if(x>y) cout << "a is the winner!";
else if(x<y) cout << "b is the winner!";
else cout << "There is no winner!";
return 0;
}
还可以判断结果是否为0,如果为0的话以后就都不用再算了。
下面是类似的做法:
#include
using namespace std;
typedef long long ll;
#define rep(i,l,r) for(int i=(l);i<=(r);i++)
#define per(i,r,l) for(int i=(r);i>=(l);i--)
const ll mod = 999068070;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
ll a,b;
cin >> a >> b;
auto solve = [](int x)
{
// if(x>=200) return 0ll;
ll r = 1;
rep(i,1,x)
{
if(r)
{
r = r*i%mod;
continue;
}
break;
}
return r;
};
ll x = solve(a),y = solve(b);
if(x>y) cout << "a is the winner!";
else if(x<y) cout << "b is the winner!";
else cout << "There is no winner!";
return 0;
}
知识点:
- 合数的性质
- 匿名函数的基本使用
- #define的妙用
题目描述
今天是Bob的生日,Alice给Bob准备了精美的礼物,但是并不想让Bob轻易的拿到这份礼物,了解到Bob最近在学习字符串,于是Alice给Bob出了一道字符串题目,打算考考他。
Alice在黑板上快速的写出了一个字符串和一个数字k,Alice想让Bob找到是否有这样一个区间,使得其中的一个字符的个数大于等于k。告诉Alice是否可以找到符合条件区间。如果可以找到请告诉Alice最短的区间长度。
正在过生日的Bob显然并不想做题但是又想拿到Alice的礼物,于是找到了你帮助他。请问你是否可以写一个程序帮助Bob。
输入描述:
第一行会给定一个t代表样例的个数(1≤t≤10),对于每组样例会有两行输入,第一行会给出一个字符串s,第二行会给出一个k。(1≤|s|≤105,1≤k≤|s|) (|s|代表字符串的长度)
输出描述: 对于每一个样例,请输出一行,如果能找到请输出最小的区间的长度,否则输出-1。
示例一:
输入:
4
aba
2
abc
2
abczgbnhe
2
abczgbnaegsca
3
输出:
3
-1
5
13
评价:
如果是遍历字符串一个一个比较是否相等两个for循环什么的话(非常暴力),那么一定会超时。毕竟数据在那摆着(但我还是尝试了…),但是有一种做法(似暴非暴),利用vector数组的形式,下面是代码:
#include
using namespace std;
typedef long long ll;
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int t;
cin >> t;
while (t--)
{
string s;
int k;
cin >> s>>k;
vector<int>v[26];//定义“二维数组”
int number[26] = {0};//初始化(将26个英文字母映射成0~25的数字)
int res = INT_MAX;//先初始化
for (int i = 0; i < s.size(); i++)
{
number[s[i]-'a']++;//遍历字符串,每个字符出现一次则在其映射的数字上加1。
v[s[i]-'a'].push_back(i);//将同一个字符的下标都存在对应映射的数字下的数组中。(类似二维数组的思想,但是可以不同列数)
}
for (int i = 0; i < 26; i++)//遍历映射的每个字母
{
if(number[i]>=k)//如果该字母的最大出现次数大于等于k
{
for (int w = k-1; w < v[i].size(); w++)//从同一字符的下标开始遍历
{
res = min(res,v[i][w]-v[i][w-k+1]+1);//每k的区间的下标数相减,求出最小区间
}
}
}
if(res==INT_MAX) cout << "-1"<<endl;//如果没有更新过res,则不存在。
else cout << res <<endl;
}
return 0;
}
知识点:
vector数组(优化暴力)的思想
题目背景:
ZYL最近迷上了机器人,看着机器人在赛道上跑来跑去,ZYL陷入了思考…
题目描述:
现在,操场上有n×n个机器人组成的方阵,每个机器人都有自己独一无二的编号i(1≤i≤n×n)。
现在,机器人教官想让他们排整齐,即第i行第j列的机器人编号恰好等于(i−1)×n+j,但教官只能发出一种指令。
指令的内容:
让方阵最外周的机器人顺时针走动一位。
例如(教官喊了一次指令):
输入描述:
输入的第一行包含一个正整数n(1≤n≤10)------代表这是一个n×n的方阵
接下来n行,每行包含n个整数a_i,j(1≤a_i,j ≤n×n)------代表原方阵中第i行第j列的机器人的编号
输出描述:
输出共一行,YES或NO.
示例一:
输入:
2
1 2
3 4
输出:
YES
示例二:
输入:
2
1 2
4 3
输出:
NO
评价:
做这题思考了好长时间,算是深刻理解了一下题意吧:内圈的数字一定是已经符合题意的顺序,不然教官让外围一直转永远也不可能“整齐”,因此不用再去考虑内圈了。因此这到题的意思就是外围无限次的顺时针移动,是否有一次可以“整齐”,若可以,则YES,否则NO。我采用的是直接的做法:只在原数组中进行移动,其中有4个临界需要判断,这个临界挺有意思的。可以在循环过程中打印出移动后的矩阵数字,看看是否为顺时针的正确移动,(我是以三阶方阵算的),最后三阶成功打印,说明临界问题处理成功,最后运行就通过了。
下面是我的代码:
#include
using namespace std;
const int N = 15;
int a[N][N];
int main()
{
ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
int n;
cin >> n;
for(int i = 1;i <= n;i++)//输入数据
for (int j = 1; j <= n; j++)
cin >> a[i][j];
for(int k = 1;k <= n*n;k++)
{//本次转换,只在一个数组中进行,因此需要提前用变量存储下临界。
bool flag = true;//每次进入循环,重置下flag
int t1 = a[1][1];
int t2 = a[n][1];
int t3 = a[n][n];
int t4 = a[1][n];
for (int i = 2; i <= n; i++)
{
if(i==n) a[1][2] = t1;//临界
else{a[1][n-i+2] = a[1][n-i+1];}//上面一行少a[1][1],从右边开始算
if(i==n) a[n][i-1] = t3;//临界
else a[n][i-1] = a[n][i];//下面一行少a[n][n],从左边算
}
for (int i = 2; i <= n; i++)
{
if(i==n) a[i-1][1] = t2;//临界
else a[i-1][1] = a[i][1];//左边少a[n][1],从上面算
if(i==n) a[n-i+2][n] = t4;//临界
else a[n-i+2][n] = a[n-i+1][n];//右边少a[1][n],从下面开始算
}
for (int i = 1; i <= n; i++)
{
for (int j = 1; j <= n; j++)
{
if(a[i][j]!=(i-1)*n+j)//判断
{
flag = false;
break;
}
}
if(!flag) break;
}
if(flag) {cout << "YES";return 0;}
}
cout << "NO";
return 0;
}
/*测试数据
3
1 2 3
4 5 6
7 8 9
*/
题目描述
真牛币提现活动的规则是这样的:初始每个人都会获得一个免费的红包,红包的金额是n(1≤n≤100且为整数),然后你可以召集你的好友帮你提高红包的额度,每一个好友都能够增加一定的额度,当红包的额度到达或超过100时,你就可以获得100真牛币了。
然而,一旁的壕哥眼神犀利了起来,他发现,第一个好友增加的额度一定是1元,随后好友增加的额度将是上一个好友增加额度的k倍(0
输入描述: 输出描述: 示例一: 题目描述 输入描述: 输出描述: 示例一: 评价: 下面是我的正确代码: 题目描述 输入描述: 输出描述: 示例一: 示例二: 评价: 题解: 题目描述 输出描述: 示例一: 示例二: 题目描述 输入描述: 输出描述: 示例一: 评价:
第一行输入一个正整数T(1≤T≤100)表示样例数量
接下来T行每一行输入两个数字,一个整型数字n(1≤n≤100且为整数)和一个浮点数k(0
每组样例每行一个输出,所有输出不包含双引号
对于每组样例如果牛哥能够提现成功,输出"YES",否则输出"NO"
输入:
3
99 0.1
98 0.5
98 0.6
输出:
YES
NO
YES
评价:
当时这道题我没做到。。。之后想到了等比数列直接求出,也可以利用等比数列前n项和的极限公式求。下面是代码:#include
I 史莱姆
讨伐魔王的路上总是充满阻碍,作为魔王的下属,为了更好的辅佐魔王,史莱姆里曼想知道自己分裂能力的最大分裂数量,已知里曼的生命值为n,里曼每次分裂可以变成两个生命值分别为a,b的自己,a,b均不为1的正整数且满足a×b=n,分裂后的史莱姆可以继续分裂,求经过最多次分裂后史莱姆的数量。
第一行:一个整数n。
数据满足:1≤n≤95718.
共一行:一个数字,表示里曼的最多分裂数量。
输入:
4
输出:
2
这道题我感觉我已经研究的非常透彻了,光题意我理解出了多种意思,最终才终于明白:一个分裂成两个的话,原有的一个可以理解成不存在了,就像细胞分裂似的,所以,当只有1个或素数个史莱姆的时候,最多次分裂(那也不能再分裂了),分裂后的数量就是它自己!
做错分析:
我对这题的题意理解不到位,总的来说,还是没理解好题意,做法方面还是可以的。#include
J 水题
众所周知,广工的宿舍没有电梯,送水的叔叔阿姨每天都需要辛苦的爬楼梯送水。
所幸的是叔叔阿姨们都配备了电动爬楼机,工作显得轻松许多。
但是广工的人太多了,即使是用机器送水还是显得不够快。
所以叔叔阿姨想让你帮忙计算一下送完n+1层楼,所需要的最少时间。
已知宿舍的楼层数为n+1,电动爬楼机一次最多可以运k桶水,叔叔用三轮车把所有需要送的水事先送到1楼(所以1楼的水就算已经送完了,不再考虑),而第i+1层需要送a_i桶水。机器和每桶水的重量皆为1,机器每次上下楼时,每1点重量就需要花费1个单位的时间,例如机器携带3桶水上一层楼需要花费4个单位时间。而且在机器上装上或卸下1桶水也需要花费1个单位的时间,假定叔叔到达某个楼层卸下水就算送达,请你计算送完所有水并且回到1楼所需要的最少时间。
第一行两个整数,第一个整数n代表有n+1楼,(1≤n≤1000000),第二个整数k代表爬楼机最大装载量为k桶(1≤k≤1000000000)。接下来一行有n个整数a,用空格隔开,a_i代表第i+1层楼需要送的水的数量,(0≤a_i≤10000)。
输出一个整数,代表送完所有水所需要的最少时间。
输入:
3 3
1 2 3
输出:
36
样例1解释
叔叔可以装3桶水,到2楼放下1桶,到3楼放下两桶,然后返回至1楼,再运三桶水送到4楼卸下返回至1楼 整个过程所花费的时间为:
3(装水) + 4(上2楼) + 1(卸1桶) + 3(上3楼) + 2(卸2桶) + 2(从3楼回到1楼) + 3(装水) + 3*4(从1楼运到4楼) + 3(卸水) + 3(从4楼回到1楼) = 36
也可以先送4楼的水,再送2楼跟3楼。
输入:
4 4
1 2 3 4
输出:
68
其实这个题对我来说,还真挺难的。我也是看了别人的代码并尝试着去模仿着写,再用测试用例去分析步骤,最后才渐渐理解的。“水题”不水呀!
首先要从这道看似复杂的题中找到"变量"与"不变量",先将不变量计算出来,再思考贪心的处理。其中的思想挺强的,还需再多思考思考。
下面是代码:#include
K 烧烤丝瓜
三哥很喜欢吃丝瓜,尤其是烤丝瓜,三哥有一个巨大丝瓜想要烤着吃。为了节约能源,厨房里的炉子在打开k分钟后自动关闭。烹饪时,三哥每隔d分钟就去厨房,如果炉子关了,她就打开炉子。当炉子关闭时,它依然保持温度,但会有所降低。众所周知,如果打开炉子,丝瓜在炉子上烹调到熟需要t分钟,如果关闭炉子,炉子依然是温暖的,则需要2t分钟。你需要弄清楚,三哥需要花多少时间来烤丝瓜。(当炉子关闭的瞬间三哥进入厨房他也会打开炉子)
输入描述:
单行包含三个整数k,d,t(0<k,d,t<10^18).
打印单个数字,烹饪总时间(分钟)。如果你的答案与正确答案的差的绝对值小于10^−8则认为正确
输入:
3 2 6
输出:
6.500000000
输入:
203954 12360 842
输出:
842.000000000
题解:
以一个周期为标准进行计算,我感觉我写的挺好理解的,但是跟人家对比起来,我的代码又有点显长,下面是我的代码:#include
L 歪脖子树下的灯
11月份不知是深秋还是早冬,寒风翻过窗户与我相会。阵阵的凉意却让我还挺舒服。楼下庭院里的树早已脱去了叶子,光秃秃的枝干张牙舞爪的伸着。在风中摇曳着一棵歪脖子树。树下挂着一盏年久失修的灯。灯是那种老式的灯,开关自然也是拉线开关。透过窗户,看着那泛黄的麻线随风飘荡,我却冒出了个奇怪的想法:假设灯的初始状态是暗的,我每拉动一次开关,灯就会有p的概率转换状态(亮->暗 或 暗->亮)。那么当我拉动n次之后,灯是亮着的概率是多少呢?
第一行一个t(1≤t≤100),代表测试数据组数。
对于每一个测试样例, 第一行有一个整数n(1≤n≤100),和一个实数p(0≤p≤1), 分别代表拉动开关的次数和灯转换状态的概率p
对于每一个测试样例,输出一个P,代表灯是亮着的概率,如果你的答案与正确答案的差的绝对值小于10^−4则认为正确
输入:
2
1 0.5
2 0.6
输出:
0.500000
0.480000
当时我想直接用公式C多少多少p的多少次方(1-p)的多少次方直接解决,但是如果是C100,1的话这个利用公式就得出现100的阶乘,且这道题也没说模多少,所以这种方法一定会爆掉。当时我就被提示出现了:浮点错误的问题。这种错误挺难找我为啥错的,之后才慢慢发现是爆掉才出现的浮点错误问题,如果当时判断的快的话,很有可能立即换一个方法,但是由于没有发现的缘故,又浪费的大量时间。
废话不多说,上代码:(挺好理解的代码呦!)#include