2020.8.15【算协集训】[第5次积分赛]

字符串five不请自来!

Problem A. 何不好的小伙伴们

描述
有一天,何不好同学和他的小伙伴们围在一起玩游戏,(带上何不好一共n个小伙伴编号分别从 0 到 n-1);
按照顺时针方向给 n 个位置编号,从 0 到 n-1。
最初,第 0 号小伙伴在第 0 号位置,第 1 号小伙伴在第 1 号位置,…,依此类推。
游戏规则如下:每一轮第 0 号位置上的小伙伴顺时针走到第 m 号位置,第 1 号位置小伙伴走到第 m+1 号位置,…,依此类推,第n − m号位置上的小伙伴走到第 0 号位置,第n-m+1 号位置上的小伙伴走到第 1 号位置,…,第 n-1 号位置上的小伙伴顺时针走到第 m-1 号位置。
现在,一共进行了 10k 轮,请问 x 号小伙伴最后走到了第几号位置。
输入数据
输入共 1 行,包含 4 个整数 n、m、k、x,每两个整数之间用一个空格隔开。 1 < n < 106
0 < m < n
0 ⩽ x ⩽ n−1
0 < k < 1018
输出数据
输出共 1 行,包含 1 个整数,表示 10k 轮后 x 号小伙伴所在的位置编号。

样例输入样例输出
10 3 4 55

分析

这题算是找规律的题?

求第 x x x 个小伙伴经过 1 0 k 10^k 10k 轮后的位置,可以先求第 0 0 0 个小伙伴在第几个位置。 1 0 k 10^k 10k 轮过后,第 0 0 0 个小伙伴走过的路程是 1 0 k ∗ m 10^k∗m 10km(经过了 1 1 1 轮, 0 → m 0→m 0m;经过了 2 2 2 轮, 0 → m → 2 m 0→m→2m 0m2m;以此类推,经过 1 0 k 10^k 10k 轮,走过的路程就是 1 0 k ∗ m 10^k*m 10km),那么,第 x x x 个小伙伴走过的路程就是 1 0 k ∗ m + x 10^k∗m+x 10km+x。 因为是转圈走的,所以总路程对每一圈的周长 n n n 取模即可,也就是说:第 x x x 个小伙伴最终走到的位置是 ( 1 0 k ∗ m + x ) % n (10^k ∗m + x)\%n (10km+x)%n

1 0 k 10^k 10k 的求解可以直接利用快速幂~

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=1e6+10;

ll n,m,k,x;

ll qpow(ll x,ll y,ll mod)	//快速幂模板
{
	ll res=1;
	while(y)
	{
		if(y&1)	res=(res*x)%mod;
		x=(x*x)%mod;
		y>>=1;
	}
	return res;
}

int main()
{
	scanf("%lld%lld%lld%lld",&n,&m,&k,&x);
	printf("%lld\n",(x%n+m%n*qpow(10,k,n))%n);
	return 0;
}

Problem B. 何不好热爱大自然

描述
何不好是出了名的喜欢大自然(因为绿色?)
前几次的榜单他没有看到他喜欢的绿色占满屏幕,一直很气愤。这次他来出一个简单题,帮助大家找回刚开始接触ACM的感觉,也让自己看到想看的一幕。
输入数据
单组输入,每组输入包含两个正整数a,b(1 ≤ a,b ≤ 1073741824)
输出数据
输出a + b的值

样例输入样例输出
1 12

分析

这题是签到题,感觉和之前写过的 A + B Problem II (HDU - 1002) 这题代码一模一样,可以直接参考我当时写的题解链接,指路 A A A 题。

代码

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=1010;
int T,cnt,lena,lenb,lenmax;
string A,B;
int a[maxn],b[maxn],c[maxn],i,sum,jin;
int main()
{
	cin>>A>>B;
	lena=A.size();
	lenb=B.size();
	lenmax=max(lena,lenb);
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	for(i=lena-1;i>=0;i--)
		a[lena-i-1]=A[i]-'0';
	for(i=lenb-1;i>=0;i--)
		b[lenb-i-1]=B[i]-'0';
	for(i=0;i<lenmax;i++)
	{
		sum=a[i]+b[i]+jin;
		c[i]=sum%10;
		jin=sum/10;
	}
	if(jin>0)
	{
		c[i]=jin;
		i++;
	}
	for(int j=i-1;j>=0;j--)
		cout<<c[j];
	cout<<endl;
	return 0;
}

Problem C. 何不好真的很爱大自然

描述
何不好是出了名的喜欢大自然(因为绿色?) 他觉得只有一排绿色不够让自己满足的,于是他决定再来一道
输入数据
单组输入,每组输入包含两个正整数a,b(1 ≤ a,b ≤ 101000
输出数据
输出a + b的值

样例输入样例输出
1 12

分析

同上题。(就 m a x n maxn maxn 的值改大了点)

代码

#include<iostream>
#include<cstring>
using namespace std;
const int maxn=10010;
int T,cnt,lena,lenb,lenmax;
string A,B;
int a[maxn],b[maxn],c[maxn],i,sum,jin;
int main()
{
	cin>>A>>B;
	lena=A.size();
	lenb=B.size();
	lenmax=max(lena,lenb);
	memset(a,0,sizeof(a));
	memset(b,0,sizeof(b));
	memset(c,0,sizeof(c));
	for(i=lena-1;i>=0;i--)
		a[lena-i-1]=A[i]-'0';
	for(i=lenb-1;i>=0;i--)
		b[lenb-i-1]=B[i]-'0';
	for(i=0;i<lenmax;i++)
	{
		sum=a[i]+b[i]+jin;
		c[i]=sum%10;
		jin=sum/10;
	}
	if(jin>0)
	{
		c[i]=jin;
		i++;
	}
	for(int j=i-1;j>=0;j--)
		cout<<c[j];
	cout<<endl;
	return 0;
}

Problem E. 何不好的博弈游戏

描述
何不好要和他的好朋友Tom玩一个游戏,游戏规则是有n个硬币,正面向上围成一个圆圈,并且排上序号1到n,确定一个数字k,每次只能翻序号连续的1到k个硬币(n和1连续),每个硬币只能翻一次,如果最后谁没有硬币可以翻(所有的硬币都被翻过一次了),谁就输掉了这个游戏,每次都是何不好先手,如果两个人都很聪明,并且没有失误的话,问最后何不好能不能赢得这次游戏。
输入数据
第一行是一个整数T(T ⩽ 10)
接下来有T行,每一行有两个整数n(1 ⩽ n ⩽ 103),k(1 ⩽ k ⩽ 103)。
输出数据
如果何不好能赢这次游戏,输出“Yes”,如果不能,输出“No”。

样例输入样例输出
3
3 1
5 2
5 6
Yes
No
Yes

分析

这题看题目也容易发现,是一道博弈论的题。更准确来说,是“对称博弈”的题。
n n n 个硬币(编号 ∈ [ 1 , n ] ∈[1,n] [1,n] )围成一圈,每次只能翻转 1 ∼ k 1 \sim k 1k 个连续的硬币。翻转最后一个硬币的人就赢了,问先手是否能赢。

我们分情况讨论一波:

  1. k ≥ n k≥n kn
    此时,先手第一次翻转的时候就可以把所有的硬币都翻完,因此一定是先手赢。
  2. k < n k<n kn
    1. k = 1 k=1 k=1
      不论先手还是后手,每次都最多只能翻转一个。因此只需要看看 n n n 为奇数还是偶数,如果是奇数就是先手赢,如果是偶数就是后手赢。
      (可以自己随便举例子,因为这里这条不是重点,就不在这再举例了)
    2. k > 1 k>1 k1
      此时,先手第一次翻转的时候不能把所有的硬币都翻完,因此要么后手全部翻完;要么后手翻和先手对称(位置对称)的硬币,使得一圈硬币变成两段数量相等的连续硬币。之后无论先手什么操作,后手都可以按照前面的两种操作进行应对,因此后手必定会赢。
      n = 5 , k = 2 n=5,k=2 n=5,k=2 为例(圆圈内的数字代表第几轮,先手和后手都翻了算一轮):

      不论是先手先取一个还是先取两个,最后都是后手赢。
      其他的可以自行举例。

代码

#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=1e6+10;

int T,N,K;

int main()
{
	scanf("%d",&T);
	while(T--)
	{
		scanf("%d%d",&N,&K);
		if(K>=N)	printf("Yes\n");
		else if(K==1)
		{
			if(N%2)	printf("Yes\n");
			else	printf("No\n");
		}
		else printf("No\n");
	}
	return 0;
}

Problem F. 何不好的数列问题

描述
何不好学了最长上升子序列和最长下降子序列以后,想到了一个问题想请你帮他解决一下。给你三个数 n x y
你需要输出一个长度长度为 n 的数列这个数列满足下面两个要求
1.最长上升子序列的长度为x
2.最长下降子序列的长度为y
如果有多组样例满足,请输出字典序最小的一组。数列的任何数字都为正整数。
输入数据
第一行一个整数 T(1 ⩽ n ⩽ 100) 有 T 组测试数据。
每组第一行输入三个个整数 n x y (1 ⩽ n ⩽ 1e5),(1 ⩽ x,y ⩽ n)。
输出数据
对于每个测试用例,第一行包含“YES”或“NO”,"NO"指示答案不存在。如果答案存在,输出"YES"另一行包含n个整数,表示你所构建的数列。

样例输入样例输出
4
10 1 10
10 10 1
10 5 5
10 8 8
YES
10 9 8 7 6 5 4 3 2 1
YES
1 2 3 4 5 6 7 8 9 10
YES
1 2 3 5 4 10 9 8 7 6
NO
#include<cstdio>
#include<algorithm>
using namespace std;
typedef long long ll;
const ll maxn=1e5+10;

ll T,n,x,y,res;
ll num[maxn],pre[maxn];

int main()
{
	scanf("%lld",&T);
	while(T--)
	{
		scanf("%lld%lld%lld",&n,&x,&y);
		if(x+y-1>n || n>x*y)	printf("NO\n");
		else
		{
			printf("YES\n");
			res=n-y;
			for(int i=1;i<=x-1;i++)
			{
				num[i]=res-(x-i-1)*y;
				if(num[i]<=0)	num[i]=1;
				res-=num[i];
				pre[i]=pre[i-1]+num[i];
			}
			for(int i=1;i<=x-1;i++)
			{
				for(int j=pre[i];j>=pre[i-1]+1;j--)
				{
					printf("%lld ",j);
				}
			}
			for(int i=n;i>n-y+1;i--)
				printf("%lld ",i);
			printf("%lld\n",n-y+1);
		}
	}
}

Problem H. 何不好之决战到天亮

描述
何不好决定亲自去救师傅昆仑侠圣婴,他向索尼克要了一把量子激光炮,震八方林世佩为了阻止他,便派了T支喽喽兵组成N*N的方阵,何不好为了尽快解救师傅,他以迅雷不及掩耳之势,闪转腾挪,运用十二个字的跑字功,转眼到了第一个方阵的左下角,端起炮来一阵猛打,但他要节省能量,最后还要对付大boss,好在该激光炮由混铁凝刚制造,发出的量子束能量无穷,能穿透无数个喽喽兵,他想知道对于每一支喽喽兵,至少要发射多少个量子束。由于效果极佳,对付每一支喽喽兵,他都跑到该方阵的左下角。
输入数据
第一行一个整数T,接下来每行一个整数N
1 <= T <= 1e6
1 <= N <= 1e6
输出数据
共T行,每行一个整数

样例输入样例输出
4
1
2
3
4
1
3
7
11
#include<cstdio>
using namespace std;
typedef long long ll;
const ll maxn=1e6+10;

ll m=0,prime[maxn],phi[maxn];
bool isprime[maxn];

void euler()
{
	phi[1]=1;
	for(int i=2;i<maxn;i++)
	{
		if(!isprime[i])
		{
			prime[++m]=i;
			phi[i]=i-1;
		}
		for(int j=1;i*prime[j]<maxn && j<=m;j++)
		{
			isprime[i*prime[j]]=true;
			if(i%prime[j]==0)
			{
				phi[i*prime[j]]=phi[i]*prime[j];
				break;
			}
			phi[i*prime[j]]=phi[i]*(prime[j]-1);
		}
	}
	for(int i=2;i<maxn;i++)
		phi[i]+=phi[i-1];
}

int main()
{
	euler();
	int T,n;
	ll ans;
	scanf("%d",&T);
	while(T--)
	{
		ans=0;
		scanf("%d",&n);
//		for(int j=2;j<=n;j++)
//			ans+=phi[j];
//		printf("%lld\n",1+2*ans);
		printf("%lld\n",2*phi[n]-1);
	}
	return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值