2020.7.18【算协集训】[第1次积分赛]


写的时候人傻的布星,实际上都不算太难的。(刚结束没多久就把基本所有题想明白了,比赛的时候不会可能主要还是因为练的太少吧)

记录一下认清five现实の第一次积分赛的题。
(有机会再补一下之前集训的题⑧)

Problem A. 真假签到题

描述
一星期一次的积分赛来了,学长们让hdy给学弟学妹们出一个毒瘤题,但是好心的学长怎 么可能会难为学弟学妹,所以决定给学弟学妹们出一个签到题,给你一个n,问你1/n是否可以 除尽,如果可以就输出YES,否则如果是一个无限小数,就输出NO;
输入数据
第一行为n(1 ⩽ n ⩽ 100000)
输出数据
输出YES或者NO;

样例输入样例输出
2YES
样例输入样例输出
3NO

分析

这道题我是列了几个数来找规律。

输入输出输入输出
1YES7NO
2YES8YES
3NO9NO
4YES10YES
5YES11NO
6NO12NO

列出足够的数(其实我写的时候列的还要多)后发现,要知道 1 n \frac{1}{n} n1 是否可以被除尽( 1 n \frac{1}{n} n1不是无限小数),只需要知道 n n n 的所有质因子是否都是 10 10 10 的质因子。

根据唯一分解定理: n = p 1 a 1 ⋅ p 2 a 2 ⋅ … ⋅ p k a k , ( p 1 < p 2 < p 3 < ⋯ < p k , a i > 0 ) n = p_1^{a_1}·p_2^{a_2} · \dots ·p_k^{a_k},(p_1<p_2<p_3<\dots<p_k,a_i>0) n=p1a1p2a2pkak,(p1<p2<p3<<pk,ai0) 10 10 10 可以分为 10 = 2 1 ⋅ 5 1 10=2^{1}·5^{1} 10=2151,它只有两个质因子 2 2 2 5 5 5 。这告诉了我们,只有当 n n n 的质因子 ∈ { 2 , 5 } ∈\{2,5\} {2,5},才能把 10 10 10(的倍数)除尽。

学长的题解就说的非常简单,只有一句话:只有满足 n=2x * 5y 才能满足1/n 可以除尽。

代码

应该有很多种写法,这里举出一例:

#include<cstdio>
using namespace std;
typedef long long ll;
int n,t;
bool flag;
int gcd(int a,int b)	//计算最大公约数
{
	return b==0?a:gcd(b,a%b);
}
int main()
{
	scanf("%d",&n);
	t=10;
	while(t!=1)	//n和t除1外没有公因子
	{
		t=gcd(n,t);
		n/=t;	//这样循环下去会让n没有因子t了
		if(t%n==0)	//注意要t%n,不能是n%t。如n=24
		{
			flag=true;
			break;
		}
	}
	if(flag)	printf("YES");
	else	printf("NO");
	return 0;
}

(加了一个可能更好理解的方法,原理是唯一分解定理)

#include<cstdio>
#include<cmath>
using namespace std;
typedef long long ll;
int n,t;
bool flag;
bool divide(int n)
{
	int m=0,p;
	flag=true;
	for(int i=2;i<=sqrt(n);i++)
	{
		if(n%i==0)
		{
			p=i;
			if(p!=2 && p!=5)
			{
				flag=false;
				break;
			}
			while(n%i==0)
			{
				n/=i;
			}
		}
	}
	if(n>1)
	{
		p=n;
		if(p!=2 && p!=5)
		{
			flag=false;
		}
	}
	return flag;
}
int main()
{
	scanf("%d",&n);
	
	if(divide(n))	printf("YES");
	else	printf("NO");
	return 0;
}

Problem B. 渣渣伟的贪玩红月

描述
众所周知,zhazhahui 代言的贪玩蓝月游戏最近很火。ypw 表示很不服气,为此,他请 来了与 zhazhahui 齐名的 zhazhawei,并委托 zhazhawei 前去 debug 他新开发的贪玩红 月小游戏,以便游戏上市后 zhazhawei 帮忙代言。zhazhawei 惊奇的发现游戏操作竟然只需 要五个键:
START GAME : Easy Middle Hard
Q : 玩家自由设定野怪队伍长度。
W:表示技能的使用次数。
E : 技能施展的左边界。
R : 技能施展的右边界。
注: 为了玩家更好的游戏体验,ypw 设置的 W 技能都是超高伤害技能炫酷的范围攻击, 什么电光一闪,十万伏特,天马流星锤,小拳拳锤你胸口了之类的,保证命中野怪必死!由于 zhazhawei 第一次 debug 这个游戏,他决定先从 Easy 版本开始!!!所谓的 Easy 版本就 是野怪全部只出现在一条水平数轴上,并且相邻野怪之间的距离都为 1。值得注意的是第一个 野怪出现在数轴 0 的位置,后续跟着出现在 1,2,3,4…,Q
输入数据
第一行有两个整数,Q(1<=Q <=100000) 和 W(1<=W<=100000),Q 与 W 之间用一个空 格隔开,接下来 W 行,每行包括两个不同的整数 E ,R,用一个空格隔开。
输出数据
输出一个数,表示 Easy 版本下野怪的存活数量。

样例输入样例输出
500 3
150 300
100 200
470 471
298

分析

这一题感觉和以前写的一个叫做Covered Points Count的题有点像。指路我当时写的题解链接
主要用到了差分的思想,当需要将 [li,ri] 区间的每一个数 +1 时,只需要修改第 li(把它+1)和第 ri+1 (把它-1)的值。

代码

我的代码:

#include<cstdio>
#include<algorithm>
#include<vector>
using namespace std;
typedef long long ll;
const int maxn=1e6+10; 
int Q,W,E,R,sum[maxn],cnt,last,num;
struct node
{
	int xb,op;
};
bool cmp(node a,node b)
{
	if(a.xb!=b.xb)	return a.xb<b.xb;
	return a.op>b.op;
}
vector<node> p;
int main()
{
	scanf("%d%d",&Q,&W);
	for(int i=0;i<W;i++)
	{
		scanf("%d%d",&E,&R);
		p.push_back((node){E,1});
		p.push_back((node){R+1,-1});
	}
	sort(p.begin(),p.end(),cmp);
	for(int i=0;i<p.size();i++)
	{
		sum[cnt]+=p[i].xb-last;
		cnt+=p[i].op;
		last=p[i].xb;
	}
	for(int i=1;i<=W;i++)
		num+=sum[i];
	printf("%d",Q-num+1);	//数轴0也会放怪物,因此野怪队伍长度是Q+1
	return 0;
}

学长的代码:

#include<bits/stdc++.h> 
using namespace std;
int s[100006];
int main()
{
     int n,m;
     cin>>n>>m;
     n = n+1;
     int a,b;
     for(int i=1;i<=m;i++)
     {
     	cin>>a>>b;
     	a= a+1;
     	b= b+1;
     	s[a]++;
     	s[b+1]--;
     }
     for(int i=1;i<=n;i++)
     {
     	s[i] = s[i-1]+s[i];
     }
     int tot = 0;
     for(int i=1;i<=n;i++)
     {
     	if(s[i] == 0) tot++;
     }
     cout<<tot<<endl;
     return 0;
} 

Problem C. 那个雨季

描述
今年的夏季,也是个多灾多难的雨季,在南方地区更是出现了连日暴雨的情况.在乡间靠 种田的阿牛所在地区也出现了暴雨天气。阿牛的田出现了问题,田与田之间的隔离带因暴雨原 因,而变得不那么坚固,导致被田里面巨量的水给冲垮。于是阿牛就想出了一个计划来弥补损失。在之前阿牛一直觉得自己田太多,但是大部分都是小田,干脆借助这次暴雨,让他的田地 汇集一下,形成满意的大田。
他的方案如下:
1.阿牛的所有田地形成了一个大矩形,共N×M块,一个田地由若干个块组成,阿牛用数字代替每一块的受灾程度,同一田地里面的数字都相同,数字越大就代表受灾越严重。
2.阿牛认为受灾最严重的田地会汇入到与其相邻的田地中,所以他会提前把这个会形成的大田地边界全部加固,防止其再汇入到其他田地。
3.阿牛拿着他写好的田地受灾程度表,循环地找下一个未加固且受灾最严重的田地,继续步骤2的操作,直到所有田地都已加固。
最后,阿牛想知道目前自己的田地数量为多少,但是又苦于计算量,想请你借助程序的力量,来回答他的这个问题。
输入数据
第一行为两个正整数N,M,代表阿牛的田地大小N×M (N ⩽ 1000,M ⩽ 1000) 接下来是N行,每行M个正整数的矩阵,每一个元素代表阿牛的田地范围每一小块的受灾程 度ai,j,并且在阿牛眼中,受灾程度相同的块,如果有上下左右相邻的关系则被认为同属一块田地,即两个相邻且受灾程度相同的田,在这场暴雨中也认为是一块田。(1 ⩽ ai,j⩽ 10)
输出数据
在经过阿牛这套方案后,最后的田地数量。

样例输入样例输出
4 5
2 3 4 4 5
3 3 4 4 5
1 2 3 2 3
5 5 4 4 4
3

样例说明
样例代表的田地情况如图1,数字相同且相邻的块,被认为是同一块田地。
先是2个受灾程度为5个田地,向外扩张,然后再是2个受灾程度为3的田地继续扩张
图1
经过阿牛方案的加固后田地情况如图2.
图2
从图中看出,最终变成3块田地,故输出3.

分析

这是一道看了想用百度翻译翻译一下到底啥意思的中文题 _ (°:з」∠) _还是后来看强子哥的题解看明白的(强子哥txdy!),这里贴一下强子哥的题解:

题目大意
有一个 n*m 的矩阵,里面相同数字并相邻的区域为一块田。从数字最大的田开始,往与其相邻的数字小的田进行扩展,扩展后形成一块田,并保持不动。接着继续重复这个过程对剩余田进行操作,直到所有的数字都不会再改变。问最后有多少个田?

题目分析
解法分为两部分:扩展 + 扩展后的田计数

  1. 扩展:
    扩展部分需要用到优先队列,最开始就把所有的坐标以及其权值 (x,y,val) 放入到优先队列中,并设置优先队列是根据 val 从大到小出队。每一个点出队的时候,尝试去改变与其相邻的四个点的 val。这里需要用到一个标记数组 tag[ ][ ]。比如过一个大田去扩展一个小田,大田的数字是 a,小田的数字是 b,大田的边界坐标会把小田与其相邻的边界坐标都改成 a,但是轮到小田的点去扩展时,它就只能把自身田里面的 b 变成 a,而不能认为自己是大田,去扩展更小的田。如果是被改变过的点,他就只能改变自身的田的数字,而不能再去改变其他田的数字了,故需要打标记。对于如何保存最终田的数字,其实很简单,在点 (x,y) 出队时候的 val,其实就是 (x,y) 的最终数字。一旦出队,剩下的扩展就完全与其这个点无关了。最开始所有的点进队 1 次,之后如果改变了,会再进队一次。所以一个点最多进两次队, 而优先队列用的堆结构,需要一个 log 时间。故时间复杂度为 O(n∗m∗log(n∗m))
  2. 计数:
    因 DFS 完成这个过程代码较短,可以采用 DFS 来完成。遍历整个矩阵,如果某个点没有被标记已访问,就计数 +1,并把改点所在的田所有点都标记成已访问。重复这个过程,最后计数的值就是田的数量。 因每个点只会被访问 1 次,所以复杂度为 O(n∗m).

代码

(代码还是我自己写的,不过和强子哥大差不差)

#include<iostream>
#include<algorithm>
#include<cstring>
#include<queue>
using namespace std;
typedef long long ll;
const int maxn=1e6+10; 
struct node
{
	int x,y,val;
	bool operator < (const node & other)const
	{
		return val<other.val;
	}
}now;
int N,M,cnt; 
int G1[1010][1010];	//原来的田 
int G2[1010][1010];	//后来的田 
int dx[4]={0,0,-1,1};	//上下左右四个方向向量的横坐标 
int dy[4]={1,-1,0,0};	//上下左右四个方向向量的纵坐标 
bool vis[1010][1010];	//true代表已被访问过,false代表没被访问过 
bool bj[1010][1010];	//true代表它被扩展,false代表它去扩展 
priority_queue<node> q;
void bfs()
{
	while(!q.empty())
	{
		now=q.top();
		q.pop();
		int x=now.x,y=now.y,val=now.val;
		if(vis[x][y])	continue;	//每个节点只能扩展一次
		vis[x][y]=true;
		G2[x][y]=val;	//出队的时候,val的值就是该点最终的值
		for(int i=0;i<4;i++)
		{
			int xx=x+dx[i],yy=y+dy[i];
			if(xx>=0 && xx<N && yy>=0 && yy<M && !vis[xx][yy])
			{
				if(!bj[x][y] && val>G1[xx][yy])	//第一种扩展方式
				{
					bj[xx][yy]=true;
					q.push({xx,yy,val});
				}
				if(bj[x][y] && G1[x][y]==G1[xx][yy])	//第二种扩展方式
				{
					bj[xx][yy]=true;//bj=true,只能扩展G1[x][y]==G1[xx][yy]表示同一块田
					q.push({xx,yy,val});
				}
			}
		}
	}
}
void dfs(int x,int y)
{
	vis[x][y]=true;
	for(int i=0;i<4;i++)
	{
		int xx=x+dx[i],yy=y+dy[i];
		if(xx>=0 && xx<N && yy>=0 && yy<M && !vis[xx][yy] && G2[x][y]==G2[xx][yy])
		{
			dfs(xx,yy);
		}
	}
}
int main()
{
	cin>>N>>M;
	for(int i=0;i<N;i++)
	{
		for(int j=0;j<M;j++)
		{
			cin>>G1[i][j];
			G2[i][j]=G1[i][j];
			q.push({i,j,G1[i][j]});
		}
	}
	bfs();
	memset(vis,false,sizeof(vis));
	for(int i=0;i<N;i++)
	{
		for(int j=0;j<M;j++)
		{
			if(!vis[i][j])
			{
				cnt++;
				dfs(i,j);
			}
		}
	}
	cout<<cnt<<endl;
	return 0;
}

Problem D. 憨憨龙的烦恼

描述
最近的几天wls特别的开心,因为他看到同学们学习特别的认真,所以打算给大家发一些 礼物来奖励下大家,每位同学收到的礼物个数相同并且最少收到1个,但是礼物的种类可以不同;
但是最近wls比较忙,于是他就让把任务分给了憨憨龙,但是憨憨龙最近参加了多校训练以后十分的自闭,他希望你能帮他解决这个问题,那样的话他会很开心的。
这个商店有n种礼物,第i种礼物有bi个,但是商店最近在做是捆绑销售,如果你买一种礼物了就必须把这种礼物全部买完;
输入数据
第一行为两个整数n和m,n个商品和m位同学 (1 ⩽ n ⩽ 20,1 ⩽ m ⩽ 104)
第二行为n个数,bi表示第 i 个商品的数量,(0 ⩽ bi ⩽ 104)
输出数据
输出一个数字,表示买到的礼物数量和。(本题保证数据答案唯一)

样例输入样例输出样例解释
4 6
1 1 2 5
6每位同学分了1个

分析

这题比赛的时候瞅了一会没明白啥意思。。
这里可以用二进制暴力枚举法/dfs的方法做,这里举二进制暴力枚举法的例子:

代码

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e6+10; 
int n,m,b[maxn],sum,ans;
int main()
{
	scanf("%d%d",&n,&m);
	for(int i=0;i<n;i++)
		scanf("%d",&b[i]);
	for(int i=0;i<(1<<n);i++) 
	{
		sum=0;
		for(int j=0;j<n;j++)
		{
			if(i>>j&1)
			{
				sum+=b[j];
			}
		}
		if(sum%m==0 && sum)
		{
			printf("%d",sum);
			break;
		}
	}
	return 0;
}

Problem E. 海王学姐的少女心

描述
海王学姐说:为了洋娃娃,我可以放弃一切
海王学姐最喜欢洋娃娃了,所以就会经常存钱买娃娃。在今天海洋学姐攒够了钱,来到了 商场。终于,在商场中海洋学姐发现了自己想要的洋娃娃,洋娃娃的价格为k,现在海洋学姐的钱包中有一沓纸币。现在海洋学姐告诉你每张纸币的面值,因为他只想从钱包中取一次钱出 来,现在他想知道,有多少种取出一沓钱的方法可以购买他想要的洋娃娃。如果取的钱多了问题不大,这样海洋学姐可以吃顿好的庆祝一下自己的新洋娃娃。但是如果钱少了,海洋学姐就 该伤心的哇哇的哭出声了。
可是海王学姐她只会喜欢洋娃娃,计算这些事当然还是交给聪明的你了。
输入数据
单组输入,第一行两个正整数,n,k(1 ⩽ n ⩽ 100000 , 1 ⩽ k ⩽ 10000)
n为海洋学姐的钱包中的纸币数量,k为洋娃娃的价格
第二行有n个由空格区分开的正整数ai表示海洋学姐钱包中每张纸币的面值( 1 ⩽ ai ⩽ 10000)
输出数据
如果不存在一种方案可以让海洋学姐买到洋娃娃,输出"WuWuWu" 如果存在至少一种可行的方案,输出海洋学姐可以买到洋娃娃的取钱的方案数

样例输入1样例输出1
3 10
2 6 1
WuWuWu
样例输入2样例输出2提示
3 5
2 6 1
4样例 2 中 4 种方案为 {6},{2,6},{6,1},{2,6,1}

分析

不知道为什么看到这题第一反应是dp(可能是那个经典的货币题的锅吧),然后没学会dp,所以思索了半天都没想明白。比赛完了之后又想了想,这题其实可以用尺取法做来着!
注:这题要开成long long,否则会WA。

代码

#include<iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+10; 
ll n,k,a[maxn];
ll l,r,sum,cnt; 
int main()
{
	cin>>n>>k;
	for(int i=0;i<n;i++)
		cin>>a[i];
	l=r=sum=cnt=0;
	for(;r<n;r++)
	{
		sum+=a[r];
		if(sum>=k)
		{
			cnt+=(n-r);
			while(l<=r)
			{
				sum-=a[l];
				l++;
				if(sum>=k)
					cnt+=(n-r);
				else
					break;
			}
		}
	}
	if(cnt!=0)	cout<<cnt;
	else	cout<<"WuWuWu";
	return 0;
}

Problem F. 快乐 beng di

描述
又是阳光明媚的一天,法外狂徒张三闲来无趣,就去找憨憨龙去蹦迪,在去的路上,两人讨论起谁请客的问题,当然,张三一点也不像花钱,他只想白嫖,于是他心生一计,对憨憨龙说,我出一个问题,如果你能回答上来的话我就请客,怎么样?憨憨龙欣然应允。问题是这样的,给 一个数N,要求快速计算出小于等于N的素数的积。
a n s = ∏ p = 2 N P ( P ∈ 素 数 ) ans=\prod_{p=2}^{N}P(P ∈ 素数) ans=p=2NP(P)
因为张三蹦迪前喝了两瓶二锅头,脑子有点迷糊,所以他说的N不超过1e7(太大了他记不住)。 憨憨龙一听这……默默的掏出了手机call你
输入数据
张三为了为难憨憨龙,他准备问他T次
第1行,一个整数T
第2行到T+1行,每行一个整数N
1<=T<=1e5
2<=N<=1e7
输出数据
一个整数,可能数太大了,所以最后结果对998244353取模

样例输入样例输出
3
2
4
7
2
6
210

分析

这题写的时候一直TLE,后来多打了一波表才过的。这就是打表的魅力吗,i了i了

代码

我的代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int maxn=1e7+10; 
ll T,N;
bool isprime[maxn];
int prime[maxn/10],m,bj[maxn];
ll sum[maxn/10];
void primes()
{
	sum[0]=1;
	for(int i=2;i<maxn;i++)
	{
		if(!isprime[i])
		{
			prime[++m]=i;
			sum[m]=sum[m-1]*i%998244353;
			bj[i]=m;
		}
		for(int j=1;i*prime[j]<maxn && j<=m;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)	break;
		}
	}
}
int mxpri[maxn];
void init()
{
	for(int i=2;i<maxn;i++)	//算i的最大质因子是什么
	{
		for(int j=i;j>=2;j--)
		{
			if(!isprime[j])
			{
				mxpri[i]=j;
				break;
			}
		}
	}
}
int main()
{
	scanf("%lld",&T);
	primes();
	init();
	while(T--)
	{
		scanf("%lld",&N);
		printf("%lld\n",sum[bj[mxpri[N]]]);
	}
	return 0;
}

学长的代码:

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll; 
typedef unsigned long ul;
typedef unsigned long long ull;
#define pi acos(-1.0)
#define e exp(1.0)
typedef pair<ll,ll> pa;
const ll INF=0x3f3f3f3f3f3f3f3f;
const ll maxn=1e7+7;
const ll Mod=998244353;
ll cnt=0,prime[maxn],N,T,sum[maxn];
bool is_prime[maxn];
void Is_prime()
{
	ll i,j;
	is_prime[0]=is_prime[1]=1;
	for(i=2;i<maxn;i++)
	{
		if(!is_prime[i])
		{
			prime[++cnt]=i;
		}
		for(j=1;j<=cnt&&i*prime[j]<maxn;j++)
		{
			is_prime[i*prime[j]]=1;
			if(i%prime[j]==0)
			break; 
		}
	}
	sum[0]=1;
	for(i=1;i<=cnt;i++)
	{
		sum[i]=sum[i-1]*prime[i]%Mod;
	}
	return ;
}
int main()
{
	ios::sync_with_stdio(false);
	Is_prime();
	cin>>T;
	ll i,j;
	while(T--)
	{
		cin>>N;
		ll pos=upper_bound(prime+1,prime+1+cnt,N)-prime;
		if(prime[pos]==N)
			cout<<sum[pos]<<endl;
		else
			cout<<sum[pos-1]<<endl;
	}
	return 0;
}

Problem G. 一起去爬山

描述
“蓝蓝的天空银河里,有只小白船。船上有棵桂花树,白兔在游玩。桨儿桨儿看不见,船 上也没帆……”一起去爬山吗
海王学姐,憨憨龙和他们的小伙伴约定一起去爬山,可是这里的山太多了,都没有名字, 但是每座山的斜度都不同,小伙伴们已经各自在半山腰,手中的仪器可以测出距离山脚的水平 距离和海拔高度,根据小伙伴们提供的信息,海王学姐需要确定他们一共爬了多少座山,请你 帮助海王学姐。
输入数据
第一行输入一个数n(1 ⩽ n ⩽ 1000),表示总人数。 接下来的n行,每行两个整数x[i],h[i](1 ⩽ x[i],h[i] ⩽ 1e18)表示第i个人距离山脚的水平距离和海拔高度。数据保证没有重复的点
输出数据
输出一个数,表示一共爬了多少座山

样例输入样例输出提示
6
1
1
2
4
3
1
3
3
3
6
4
8
3这里的六个人一共爬了三座山
第一个人和第四个人在一座山上
第二个人,第五个人和第六个人在一座山上
第三个人在一座山上

分析

这题用到了(我)以前从来没有用过的long double,如果不用这个的话事情就会变得非常麻烦。。但是一旦用了,就发现其实这道题不难辽。

因为每座山的斜度不同,因此只需要计算这些山的 t a n θ = h x tanθ=\frac{h}{x} tanθ=xh ,用 m a p map map 来计数就可以了。

代码

我的代码:

#include<iostream>
#include<algorithm>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1e6+10; 
int n,sum;
map<long double,int> mp;
long double x,h;
int main()
{
	cin>>n;
	while(n--)
	{
		cin>>x>>h;
		mp[h/x]++;
	}
	for(auto it=mp.begin();it!=mp.end();it++)
		sum++;
	cout<<sum<<endl;
	return 0;
}

学姐的代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1234567;
ll x[maxn],h[maxn];
int n;
int main()
{
	cin>>n;
	map<pair<ll,ll> ,ll> ma;
	for(int i=0;i<n;i++)
	{
		cin>>x[i]>>h[i];
		ll mid=__gcd(x[i],h[i]);
		ll dx=x[i]/mid;
		ll dh=h[i]/mid;
		ma[{dx,dh}]++;
	}
	cout<<ma.size()<<endl;
	return 0;
}

Problem H. 小明们的工资

描述
小明的公司有T个员工,他们是小明1号,小明2号,小明3号…小明T号。小明的公司有一 个奇怪的规定,每个人的工资都是随机决定的。决定工资的规则如下:小明公司里的每个人都 要随机选择两个非负整数n和m,他的工资就是nm%Mod。Mod=1e7+19。另外小明公司里的每 一个人都非常的喜欢素数,如果他们的工资是素数,就会高兴的大喊“yes”,否则会沮丧的说 “no”。现在请你计算出小明公司里每个人的工资,并判断他们会说“yes”还是“no”。
8输入数据*
输入一个正整数T,代表有T(T<5000)组数据。接下来的T行,每行有两个非负整数n和m。 (0 <= n,m < 1e9)
输出数据
输出有2T行,对于每组数据,第一行是工资,第二行是yes或者no

样例输入样例输出
3
2 4
3 1
4 1
16
no
3
yes
4
no

分析

在这道题上贡献了好几发WA。。被自己傻到,当时就是没有注意到要说明一下 0 0 0 1 1 1 不是素数!其他的就套用一下模板就可。

代码

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;
typedef long long ll;
const int maxn=1e7+100; 
const int Mod=1e7+19;
int T;
ll N,M,sum;
ll poww(ll x,ll y,ll mod)	//快速幂板子
{
	ll tmp=1;
	while(y)
	{
		if(y&1)	tmp=(tmp*x)%mod;
		x=(x*x)%mod;
		y>>=1;
	}
	return tmp;
}
bool isprime[maxn];
int prime[maxn/10],m;
void primes()	//欧拉筛板子
{
	isprime[0]=isprime[1]=true;	//要记得说明一下
	for(int i=2;i<maxn;i++)
	{
		if(!isprime[i])
			prime[++m]=i;
		for(int j=1;i*prime[j]<maxn && j<=m;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)	break;
		}
	}
}
int main()
{
	scanf("%d",&T);
	primes();
	while(T--)
	{
		scanf("%lld%lld",&N,&M);
		sum=poww(N,M,Mod);
		printf("%lld\n",sum);
		if(!isprime[sum])
			printf("yes\n");
		else
			printf("no\n");
	}
	return 0;
}

Problem I. 罗魂阵

Problem Description
一天,你正在快乐星球八号实验室研究你的新发明,突然你收到乐乐通过项链给你发的语 音消息,他被困在了树林里,通过定位和图像检索数据库你发现他进入了山林里的一个千年古阵——罗魂阵。
“多面体,我发现了一个石碑,上面写着:按照一定的顺序在规定时间内解开每棵树上的谜题,就能成功破解此阵。每一棵树上都刻有一串字符,只需要按照下面的规则,把十进制数表示出来,并按照十进制从小到大的顺序,依次将转换后的十进制数刻在对应树上,便能破解罗魂阵。
具体规则是:
A 1
B 5
C 10
D 50
E 100
F 500
G 1000
将每一个字符所对应的十进制数写出并加在一起就能得到对应的十进制。
例如:FCAA=500+10+1+1=512
这些字符总体上是按照字符所对应的十进制数的大小,从大到小排列的。需要注意的是有一个特殊的规则:
如果该符号的前面出现了一个比它要小的符号,则用后一个字符所对应的十进制数减去前 一个字符所对应的十进制数,得到的一个数再加入总答案中。(保证最多只会有连续两个字符 从小到大排列,不会存在ABC等三个及以上的连续)
例如:AB=5-1=4
按下石碑上的开关即开始挑战,所以你需要提前帮乐乐算出来所有树上的答案。作为全宇 宙最聪明的小发明家,请你写一段程序来帮乐乐快速计算出答案。
Input
输入包含n+1行,第一行输入n(1 ⩽ n ⩽ 3000)表示有n个树上面有谜题,第2 ∼ n + 1行每 行有一个字符串,表示n个树上的的谜题。
Output
1行,n个十进制数,由空格间隔,并按照十进制从小到大输出。

Sample InputSample OutputHint
5
AAA
AB
GEGCEAB
AC
DBAAA
3 4 9 58 1994GEGCEAB = G + EG + CE + AB = 1000 + 900 + 90 + 4 = 1994

分析

这题其实也挺简单的,用map实现字母与数字间的对应就可以。蓝鹅刚开始写的时候把 m a p map map 的键定义成了 s t r i n g string string 就出错了呢,改成 c h a r char char 就好了。

代码

#include<iostream>
#include<algorithm>
#include<cstring>
#include<map>
using namespace std;
typedef long long ll;
const int maxn=1e5+10; 
int n,sum,len,cnt,s[maxn]; 
string str;
map<char,int> mp;
int main()
{
	cin>>n;
	mp['A']=1;mp['B']=5;mp['C']=10;mp['D']=50;mp['E']=100;mp['F']=500;mp['G']=1000;
	for(int i=0;i<n;i++)
	{
		cin>>str;
		len=str.size();
		sum=0;
		for(int j=0;j<len;j++)
		{
			if(str[j]>=str[j+1])	sum+=mp[str[j]];
			else
			{
				cnt=mp[str[j+1]]-mp[str[j]];
				sum+=cnt;
				j++;
			}
		}
		s[i]=sum;
	}
	sort(s,s+n);
	for(int i=0;i<n;i++)
	{
		cout<<s[i];
		if(i!=n-1)	cout<<" ";
	}
	cout<<endl;
	return 0;
}

Problem J. 乘风破浪的H学长

描述
H学长太强了,因为他总是会选最优策略应对各种问题,所以从来不会输,但今天还是有n个同学来找他battle。初始n个人排成一队分别编号1到n,已知第i个人的战力值为ai,H学长每战胜一个编号为i的人会消耗i∗ai的体力值,当第i个人出局后,在此人之后的每个人会自动往前补齐空缺位置(编号减一)。请你帮助H学长算算战胜这n个人消耗的体力值总和最小是多少?
输入数据
第一行一个整数n (1 ≤ n ≤ 106) 第二行n个整数a1···an (|ai|≤ 106)
输出数据
一个整数,H学长消耗体力值总和的最小值。

样例输入样例输出H学长的温馨提示
3
3 2 1
6因为有同学战力值为负值,所以答案可能为负数。

分析

我们可以看到输入的数据有可能是正的也有可能是负的,然后要求消耗的体力值总和的最小值。
如果数据全是正数,那么我们应该从前往后开始战斗。因为按照顺序来的话,第一个人被打败了,共耗费了 a [ 1 ] a[1] a[1] ,第二个人的编号会从 2 2 2 变成 1 1 1 ,第三个人的编号会从 3 3 3 变成 2 … 2 \dots 2 然后再打败第二个人,共耗费了 a [ 1 ] + a [ 2 ] a[1]+a[2] a[1]+a[2] ,第三个人的编号会从 2 2 2 变成 1 1 1 ,第四个人的编号会从 3 3 3 变成 2 … 2 \dots 2 以此类推,最后得到的效果就是每个人的战力值之和。
如果数据全是负数,那么我们应该从后往前开始战斗。因为负数乘上正数会得到一个更小的负数,这样就能使 ∑ i = 1 n ( i ∗ a [ i ] ) \sum_{i=1}^{n}(i*a[i]) i=1n(ia[i]) 的值就能达到最小。
如果数据有正有负,那么结合我们上面讲的,只需要分开判断正负数的情况就好了。

代码

我的代码:

#include<cstdio>
#include<iostream>
#include<algorithm>
#include<cstring>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;
ll n,sum,a[maxn];
int main()
{
	scanf("%lld",&n);
	for(int i=1;i<=n;i++)
		scanf("%lld",&a[i]);
	sum=0;
	for(int i=n;i>=1;i--)
	{
		if(a[i]<0)
			sum+=(i*a[i]);
	}
	for(int i=1;i<=n;i++)
	{
		if(a[i]>0)
			sum+=a[i];
	}
	printf("%lld",sum);
	return 0;
}

学长的代码:

#include<iostream>
using namespace std; 
int main()
{
	long long ans=0, n, t;
	cin>>n;
	for(int i = 1;i <= n;i ++)
	{
		cin>>t;
		if(t>0) ans += t;
		else ans += i*t;
	}
	cout<<ans<<"\n";
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值