月赛题目:
1.无限增长
有一个无限长的数轴,小IT想要知道某个点是否在数轴上。
起始时,数轴上只有一个点1,数轴遵循这样一种特点:如果数 x 在数轴上,那么x⋅a和x+b也在数轴上。
现在,小IT给定a,b,想问你n是否在数轴上,如果在输出
Yes
,否则输出No
。输入格式:
第一行为一个整数T,表示T(1≤T≤10^5)组数据
对于每一组数据
在一行中输入n,a,b,(1≤n,a,b≤10^9)
输出格式:
对于每一组测试数据,在一行中输出Yes或者No
输入样例:
5 24 3 5 10 3 6 2345 1 4 19260817 394 485 19260817 233 264
输出样例:
Yes No Yes No Yes
样例解释:
对于样例中的24,由于1在数轴上,所以3在数轴上。
由于3在数轴上,所以8在数轴上。
由于8在数轴上,所以24在数轴上
思路:
找规律:
#include<iostream>
using namespace std;
//( n - a^x ) % b 余数不为0
int n, a, b, t;
int main()
{
cin >> t;
for(int i = 1; i <= t; i++)
{
long long s = 1;
cin >> n >> a >> b;
if(a == 1)
{
if((n-1)%b == 0) cout << "Yes\n";
else cout << "No\n";
}
else
{
int flag = 1;
while(s <= n)
{
if((n-s)%b == 0)
{
flag = 0;
cout << "Yes\n";
break;
}
s = s*a;
}
if(flag) cout << "No\n";
}
}
return 0;
}
2.石头剪刀布
小IT想起了小时候经常玩的小游戏——石头剪刀布!于是他叫来了2⋅n 位儿时的小伙伴~,进行了m轮比赛。小IT将根据获胜的场数进行排名!当进行第i轮比赛时,此时排名为2⋅k−1和2⋅k的朋友进行比赛(k=1,2,…,*n),当分数相同时时,ID更小的排名靠前。
输入格式:
第一行输入两个整数n(1≤n≤50) 和 m(1≤m≤100)
接下来n行 每行输入m个字符Aij,Aij为
S
,P
,R
其中一个。
S
代表剪刀,P
代表布,R
代表石头。输出格式:
输出2⋅n行
第i行表示排名为i的人的id号。
输入样例:
2 3 RSP PPP SSS PPS
输出样例:
3 1 2 4
样例解释:
在第一轮比赛中,1和2之间以及3和4之间进行比赛。2赢得前者,3赢得后者。
在第二轮比赛中,2号和3号之间以及1号和4号之间进行比赛。3赢得前者,1赢得后者。
在第三轮比赛中,3号和1号之间以及2号和4号之间进行比赛。玩家3赢得前者,玩家4赢得后者。
因此,最终,排名顺序如下:3,1,2,4,从高到低。
字符串第一列为第一轮比赛,这些人出的是什么,第二列为…以此类推。
最后是按照分数的高低来排序的,如果分数相同则编号小的排在前面
//java写多了,命名都比较书面化,变量名可能看起来长,导致代码看起来长
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
const int N = 150;
//面向对象,把属性封装在一个结构体里面
typedef struct{
int no;//编号
int goal;//得分
string s;//出的SPR
}People;
People peo[N];
//判断a,b第lun轮的结果
int Win(People a, People b, int lun);
//根据分数排序
bool cmp( People a, People b);
int main()
{
int n, m;
cin >> n >> m;
int maxnum = 2 * n; //人数
for ( int i = 1; i <= maxnum; ++i ) {
peo[i].no = i, peo[i].goal = 0;
cin >> peo[i].s;
}//初始化
for ( int i = 1; i <= m; ++i ) { //共有m轮比赛
for ( int j = 1; j <= n; ++j ) {
int win = Win(peo[j*2-1], peo[j*2], i);//第i轮结果
if ( win == 1 ) peo[j*2-1].goal++; //前面的赢了
else if ( win == 0 ) peo[j*2].goal++; //后面的赢了
}
sort(peo+1,peo+1+2*n, cmp);//每一轮根据分数排个序
}
for ( int i = 1; i <= maxnum; ++i ) {
cout << peo[i].no << endl;
}
return 0;
}
int Win(People a, People b, int lun)
{
char aa = a.s[lun-1], bb = b.s[lun-1];//lun-1因为string是从0开始计数的
if ( aa == bb ) return -1; //平局不计分
else if ( aa == 'S' && bb == 'P' ) return 1;//返回1就是a赢了b
else if ( aa == 'P' && bb == 'R' ) return 1;
else if ( aa == 'R' && bb == 'S' ) return 1;
return 0;//a输给了b
}
bool cmp( People a, People b){
if ( a.goal != b.goal ) return a.goal > b.goal;
return a.no < b.no;
}
3.上号
众所周知,PTA是一款大型竞技游戏,里面不仅有单人副本(固定题目集),还有紧张刺激的多人实时在线PK(比赛),一经推出,广受好评。现在,小IT得知了平台共有 N 位已经注册过的玩家(学生?玩家!),并且给出每位玩家的在哪一天上号,并且持续在线多少天。小IT找到了你,想让你告诉他,有多少天只有一位玩家在线,多少天有两位玩家在线…有多少天有 N 位玩家在线。\
输入格式:
第一行给出一个 N(1≤N≤2∗10^5),表示玩家的数量。 接下来 N 行,每行两个整数x,y(1≤x,y≤10^9),分别表示在哪一天上线和持续在线多少天。
输出格式:
输出一行,包含 N 个用空格隔开的整数,行末也有空格。第 i个整数表示在同时有 i位玩家在线的天数。
输入样例:
3 1 2 2 3 3 1
输出样例:
2 2 0
样例解释:
第一位玩家分别在第1,2天在线。
第二位玩家分别在第2,3,4天在线。
第三位玩家在第3天在线。
第一天只有第一位玩家在线,第4天只有第二位玩家在线,一共两天,所以输出2。
第二天和第三天都有两位玩家在线,所以输出2。
三位玩家同时在线的天数为0,所以输出0。
题目意思好理解,处理一遍数组之后,按照数组值分类,每一类有多少个。
差分,需要用到亿点点技巧的差分;
先来说说段错误的代码
#include<iostream>
#include<bits/stdc++.h>
using namespace std;
int n, k=0, a[200005]={0};
map<int,int> mp;
int main(void)
{
cin >> n;
for(int i = 0; i < n; i++)
{
int x,y, s = 0;
cin >> x >> y;
s=x+y-1;
if(s > k) k = s;
a[x]++;
for(int j = 1; j <= y-1; j++)
a[x+j]++;
}
for(int i = 1; i <= k; i++)
{
mp[a[i]]++;
}
for(int i = 1; i <= n; i++)
cout << mp[i] << " ";
return 0;
}
其实上面的代码不是段错误的话就是超时!!
先看题意,每行两个整数x,y(1≤x,y≤10^9 ),分别表示在哪一天上线和持续在线多少天。x,y都在10的九次方以内,也就是说假如有个玩家在第10 ^ 9天上号,持续上号了10 ^ 9天,那么数组空间就要到10^9级别,很明显这是开不了这么大的!!!段错误的由来,
再说超时,用普通的差分,需要遍历完1到10^ 9,这也超时了,上面的代码两层for循环,远超于10^9,更是超时了. 超时的由来
既然不能普通的差分,那就需要用到差分亿点点技巧:(STL中的map)
先来看这样一个例子:
上图中值为1的个数0,值为2的个数0,值为3的个数5。
我们都知道,差分数组的前缀和就是上图中第二行数组的值。
普通的差分数组需要吧下标1-6的值全部存起来,假如玩家都是在第一天上号1,上号10^ 9天,按照普通的思路要开10^9个空间大小,能不能把中间的0的空间给省下来呢?差分数组值为0,就表示两数之间相差为0,即两数相等,相等的数有几个呢?(6 - 1 = 5 个数相等,),并且这五个数都是3,3是什么?是差分数组一串0前面第一个下标的前缀和,上图中四个0前面第一个下标是1,前缀和就是3。
即 结果数组[前缀和] += 相等的个数; 下标1的前后缀和是3, 3相等的有5个就是有三位玩家一起在线的有五个人。
用map来存,就可以把中间大部分的0给省掉,我们只存了有下标的,就是只存了处理差分过程中出现的下标 ( 就是m[online]++,m[online+d]-- 代码这里 ) ,上图给的例子中打钩的就是map中存的。
还请看代码注释细细评味
#include <iostream>
#include <map>
using namespace std;
const int N = 2*1e5+5;
int day[N];//结果数组
//第一个int存下标,第二个int存差分数组的值
map<int,int> m; //差分
int main()
{
int n;
cin >> n;
for ( int i = 1; i <= n; ++i ) {
int online, d;
cin >> online >> d;
//预处理差分,
//map是按key来排序的,所以这个map的key是从小到大排序的。
m[online]++;
m[online+d]--;
}
//sum表示前缀和
int sum = 0, prevday = 0;//我们是从1开始计数的,0开始初始化对结果无影响
//temp表示map<int,int> 中的一个个元素,所以temp.first表示的就是下标,temp.second表示的是值
for ( auto &temp : m ) {//c++11方式的遍历
//eq,表示相等的个数
int eq = temp.first - prevday;
day[sum] += eq; //为什么是+=?因为可能后面还有的前缀和==sum
/*看下面例子:
下标 : 1 2 3 4 5 6 7
差分 数组: 1 0 0 -1 0 0 1
前缀和数组 1 1 1 0 0 0 1 前面有三个1,后面还有一个1
*/
sum += temp.second; //前缀和,
prevday = temp.first;
}
for ( int i = 1; i <= n; ++i ) {
cout << day[i] << ' ';
}
return 0;
}
差分,前缀和资料:好东西
链接:https://pan.baidu.com/s/1L4jmbBH1-DaLmLmzGKEefQ
好东西
提取码:txf5