SDUT 2021 Spring Individual Contest(for 20) - 9(2)(部分)补题

A - hzy 和zsl 的生存挑战

zsl 和hzy 来到了臭臭城堡,打算挑战臭臭城堡的大魔王hyz,大魔王hyz设置了这样的一个挑战:

  1. zsl 和hzy两个人各自来到一间密室,期间两人无法以任何形式交流
  2. 大魔王hyz会随机在两个人的脑海里各发送一个数字,0或者是1
  3. zsl 和 hzy 需要猜对这俩个数字才算通关,但是大魔王hyz觉得人生不能过于无敌,因此降低难度,只要两个人中有一个人答对就算是通关

现在大魔王hyz 给出的数字可能的情况有 00, 01, 10, 11 四种,请按上述枚举的顺序,计算所有情况下zsl 和hzy 通关的几率。(假设zsl 和 hzy 两个人都足够机智,能够选择出最优决策)
Input
(空)
Output
输出四个答案,每个答案后面跟随一个换行符并且保留两位小数位,分别对应00,01,10,11的情况下,zsl和hzy通关的几率
Sample Input
(空)
Sample Output
1.00
0.00
0.50
0.55 (输出仅做格式参考,不保证正确性)
这两个人只要在进房间前商量一下。
策略就一定能过策略就是,其中一个人把看到的数字重复两遍,而另一个人根据自己看到的数字,说出自己看到的数字和另一个数字。
比如魔王给了两人0,0。那zsl看到了0,就会想到这对数字可能是0,0或者是0,1,而hzy就会想到可能是0,0或者1,0。zsl说出00,hzy说出10
魔王00,zsl说00,hzy说10
魔王01,zsl说00,hzy说01
魔王10,zsl说11,hzy说10
魔王11,zsl说11,hzy说01,所以必赢。
下面附上代码

#include<iostream>
using namespace std;
int main()
{
    
    
	printf("1.00\n");
	printf("1.00\n");
	printf("1.00\n");
	printf("1.00\n");
	return 0;
}

B - 人类史上最大最好的希望事件

作为CNCS的半壁江山,狗哥常常在宇宙中心邵阳眺望黄浦江,夜晚的星空总是迷人,有时候还能见到彗星滑落。

狗哥是幸运的,他在两秒钟内看到了十七颗彗星划过天际,作为打ACM的学者,自然不会有「稳定-1」情况。他开始研究彗星运动的轨迹,发现他们都遵照斐波那契螺旋线在运动着。

尤里卡!狗哥觉得这就是找寻「生命,宇宙和一切的终极答案」的精要所在,但是怎么表示呢?狗哥觉得求取斐波那契螺旋线经过的一个个方格的面积之和就是公式的表现。

例如下图,螺旋线每划过一个方格,都转过了四分之一圈。如果我们以四分之一圈为单位,那么我们用类似带分数的形式表示螺旋线转动的起点和终点。例如,0+0 到 0 + 1 意即从第一个方格转到第二个方格,划过了前两个方格,他们的面积之和为2(1+1)。同理,0+0 到 1+0 划过了前五个方格,他们的面积之和为40(1+1+4+9+25)。
在这里插入图片描述
但是聪明的狗哥需要一个程序去获得指定范围内的螺旋线面积之和,狗哥给了你一首「希望之花」的时间,而他需要利用这个时间去打出四暗刻单骑。如果你能完成这个程序,狗哥会封你为格拉摩根伯爵
Input
不定组数据。

首先输入一个整数Q,代表狗哥询问次数。

接下来Q行,每行四个整数a,b,c,d,代表狗哥想求 a+b 到 c+d 之间的螺旋线面积之和。

1<= Q <= 10000

0<= a,c <= 10000

0 <= b,d <= 3

结果对192600817取模。
Output
一个数字,表示螺旋线面积之和。
Sample Input
4
0 0 0 1
0 0 1 0
1 2 2 1
1 1 0 3
4
0 0 0 1
0 0 1 0
1 2 2 1
1 1 0 3
Sample Output
2
40
4791
98
2
40
4791
98
涉及:【前缀和+斐波那契】
分析: 对于起点和终点表示法的解析(这个表示法太难懂了 ),其实类似于四进制的表示法。例如0+1 0+2 0+3 1+0 1+1 1+2(这里除去0+0表示的方格) 分别表示第1个方格,第2个方格,第3个方格,第4个方格,第5个方格,第6个方格。

这里也可以把0+0->1+0,就是转了一整圈,每转1圈,前面数+1,转的圈数<1,则加后面的数,理解方法就因人而异了。

经过分析得知a+b代表第4*a+b个方格。
把斐波那契对应的方格面积带进去,再运用前缀和即可求得答案

坑点: 因为数据都mod过,可能会出现 s【x】-s【y】为负数的情况,即使x<y。所以输出的时候相减加个mod再取余就不会WA。以后这种题目都加个mod吧,加了不可能错,不加却有可能。
(这里有个疑惑,我选了C++WA了,选G++却AC了,还请大佬指点迷津)

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int mod=192600817;
const int N=40004;
ll a[N],s[N];
void solve()
{
    
    
	a[0]=1,a[1]=1;
	s[0]=1;
	s[1]=2;
	for(int i=2;i<N;i++)
	{
    
    
		a[i]=a[i-1]%mod+a[i-2]%mod; //这里为了防止爆long long,所以在计算中直接进行取模运算了 
		s[i]=((a[i]*a[i])%mod+s[i-1]%mod)%mod;
	}
	
}
int main()
{
    
    
	int n;
	solve();
	while(~scanf("%d",&n))
	{
    
    
		int a,b,c,d;
		while(n--)
		{
    
    
			scanf("%d%d%d%d",&a,&b,&c,&d);
			int x=a*4+b;
			int y=c*4+d;
			if(x<y) swap(x,y);
			printf("%lld\n",(s[x]-s[y-1]+mod)%mod); //这里+mod,就是因为之前已经取模运算,可能会出现负数的情况 
		}
	}
}

C - 超级无敌简单题

通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
通常来说,题面短的题目一般都比较难,所以我要把题面写得很长很长。
鸽子数字由以下过程定义:从任何正整数开始,将数字替换为其各个数位的平方和,并重复该过程,直到该数字等于1。如果不能,则这个数字不是鸽子数。
例如7是鸽子数,因为7->49->97->130->10->1。(77=49,44+99=97,99+7*7=130…如此类推)
显然1是第一个鸽子数。
有Q个询问,每个询问给出一个数k,你需要输出第k个鸽子数。
Input
第一行一个Q,代表询问的个数(Q<=100000)
接下来Q行,每行一个数字k(k<150000)
Output
每行输出一个数,代表第k个鸽子数
Sample Input
2
1
2
Sample Output
1
7
这题直接打表,把前150000个鸽子数全部打出来,再输出答案。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
const int N=150000+5;
ll num[N];
void solve()
{
    
    
	ll a=1,cnt,now,ans; //a用来计数,cnt用来计算一个数按照题目要求的变成格子数需要的操作数,now是代判断的数,ans用来把各位数平方求和用的 
	for(ll i=1;i<=1000000;i++) //这个1000000是在保证不超时的情况下,试出来的数 
	{
    
    
		cnt=0;
		now=i;
		while(cnt<=15) 
		{
    
    
			cnt++;
			ans=0;
			while(now)
			{
    
    
				ans+=(now%10)*(now%10);
				now/=10;
			}
			now=ans;
			if(now==1) break;
			if(now==4) cnt=16; //数字出现4的话一定不会是鸽子数(用来减少时间) 
		}
		if(cnt<=15) //循环次数少于15次说明是鸽子数 
		{
    
    
			num[a++]=i;
		}
	}
}
int main()
{
    
    
	solve();
	int t;
	scanf("%d",&t);
	while(t--)
	{
    
    
		int n;
		scanf("%d",&n);
		printf("%d\n",num[n]);
	}
	return 0;
}

zyb的面试

今天zyb参加一场面试,面试官听说zyb是ACMer之后立马抛出了一道算法题给zyb:
有一个序列,是1到n的一种排列,排列的顺序是字典序小的在前,那么第k个数字是什么?
例如n=15,k=7, 排列顺序为1, 10, 11, 12, 13, 14, 15, 2, 3, 4, 5, 6, 7, 8, 9;那么第7个数字就是15.
那么,如果你处在zyb的场景下,你能解决这个问题吗?
Input
T组样例(T<=100)
两个整数n和k(1<=n<=1e6,1<=k<=n),n和k代表的含义如上文
Output
输出1-n之中字典序第k小的数字
Sample Input
1
15 7
Sample Output
15
我们可以发现这就是个十叉树,每次只需要找比当前大的十倍的数有多少,够不够k,比如说15 7,那么在1的时候,1的儿子有6个数,发现加起来正好与k相同,那么就说明需要的数在这些数里面,继续往下,以此类推。
在这里插入图片描述
以此图来说明,先计算从1到2的步数,在同层来说就是步数直接+1,也就是min(13+1,2)-1=1。在从1结点开10个子节点,也就是10-19()n1变成了10,n2变成了20,这时步数就再+4,即min(13+1,20)-10=4。所以总步数就是5,这里的-10就是不走1-9,直接从1往下一层走。
下面这个代码是仿照了csdn的一位大佬的,不是本人原创。

#include<iostream>
#include<cstring>
#include<algorithm>
using namespace std;
#define ll long long
int get(int n,ll n1,ll n2)
{
    
                 
	int step=0; 
	while(n1<=n) //如果后面开创的结点数量超过了本来就有的结点的数,那就说明实际没有那些结点,不能再往下走了 
	{
    
    
		step+=min((ll)n+1,n2)-n1;
		n1=n1*10; //走完之后开创下面的10个子节点
		n2=n2*10; 
	}
	return step;
}
int find(int n,int k) //这个函数就是求步数
{
    
    
	int now=1;
	while(k!=1)
	{
    
         //计算从n1到n2要走的步数
         //例:第一轮计算从1到2的步数,然后判断走的步数和k的大小,判断是在1和2之间还是2之后
		int step=get(n,now,now+1); 
		if(step<k) //需要向右节点移动
		{
    
              
			now+=1; //往右
			k-=step;//先把下一层走完,再往右走
		}
		else      //目标值在节点1和节点2之间,要向下移动
		{
    
    
			now*=10;
			k-=1;  //走一步
		}
	}
	return now;
}
int main()
{
    
    
	int t;
	scanf("%d",&t);
	while(t--)
	{
    
    
		int n,k;
		scanf("%d%d",&n,&k);
		printf("%d\n",find(n,k));
	}
	return 0;
}

To be continued

猜你喜欢

转载自blog.csdn.net/weixin_54699118/article/details/115149454