月赛题目1025

月赛题目:

1.无限增长

有一个无限长的数轴,小IT想要知道某个点是否在数轴上。

起始时,数轴上只有一个点1,数轴遵循这样一种特点:如果数 x 在数轴上,那么xax+b也在数轴上。

现在,小IT给定a,b,想问你n是否在数轴上,如果在输出Yes,否则输出No

输入格式:

第一行为一个整数T,表示T(1≤T≤10^5)组数据

对于每一组数据

在一行中输入nab,(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个字符AijAijS,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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值