CodeForces - 492D Vanya and Computer Game (一道数学思维题,可以说是最大公约数,最小公倍数为一个周期,再找在这一个周期中能打多少拳,拳数的周期)

Vanya and his friend Vova play a computer game where they need to destroy n monsters to pass a level. Vanya's character performs attack with frequency x hits per second and Vova's character performs attack with frequency y hits per second. Each character spends fixed time to raise a weapon and then he hits (the time to raise the weapon is 1 / x seconds for the first character and 1 / y seconds for the second one). The i-th monster dies after he receives ai hits.

Vanya and Vova wonder who makes the last hit on each monster. If Vanya and Vova make the last hit at the same time, we assume that both of them have made the last hit.

Input

The first line contains three integers n,x,y (1 ≤ n ≤ 1051 ≤ x, y ≤ 106) — the number of monsters, the frequency of Vanya's and Vova's attack, correspondingly.

Next n lines contain integers ai (1 ≤ ai ≤ 109) — the number of hits needed do destroy the i-th monster.

Output

Print n lines. In the i-th line print word "Vanya", if the last hit on the i-th monster was performed by Vanya, "Vova", if Vova performed the last hit, or "Both", if both boys performed it at the same time.

Example
Input
4 3 2
1
2
3
4
Output
Vanya
Vova
Vanya
Both
Input
2 1 1
1
2
Output
Both
Both
Note

In the first sample Vanya makes the first hit at time 1 / 3, Vova makes the second hit at time 1 / 2, Vanya makes the third hit at time 2 / 3, and both boys make the fourth and fifth hit simultaneously at the time 1.

In the second sample Vanya and Vova make the first and second hit simultaneously at time 1.


题意:就是输入三个数n,x,y, x 代表着Vanya一秒中能打x拳,y代表着 Vova一秒中能打y拳, n代表下面的测试数据有多少组,下面给你n组测试数据,每组测试数据代表着这个怪打多少下就死了,输出是谁打死的;

思路一:x 代表着 Vanya 一秒中能打x拳,y代表着Vova 一秒中能打y拳,那么1/x 秒Vanya 能打一拳,1/y秒 Vova 能打一拳,同时扩大x*y倍,不就是,Vanya  y 秒能打一拳,Vova x 秒能打一拳,他们两个从0开始计时,到下次在同一时刻各打出一拳,那么这一时刻,一定是x和y的公倍数,那么最小的也就是最小公倍数了,最小公倍数为时间周期,那么在这个时间周期中能打这个怪多少拳呢?我开始以为是x+y拳,其实不是,最小的拳数周期为  x/gcd(x,y) + y/gcd(x.y) ;举个例子  在一定时间中,Vanva 能打2拳,Vova 能打 6 拳,那么当Vanva能打1拳时,Vova能打3拳,所以他们的拳数最小周期为4而不是8;

代码:

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;

#define Max 2000010

long long  book[Max];  // 标记一个拳数周期  

long long gcd(long long a,long long b)
{
	if(b==0)
		return a;
	else 
	{
		long long tt = gcd(b,a%b);
		return tt;
	}
}

int main()
{
	long long  i,j,k1,k2,m,n,pp,k;
	while(~scanf("%lld%lld%lld",&k,&m,&n))
	{
		long long tt = m*n;
		long long g = gcd(m,n);  //最大公约数; 
		long long kk = m/g+n/g; //达到最大公约数之前会出现打多少拳,一个周期; 
		tt=tt/g;     // tt 为最小公倍数;(只有达到公倍数时,a人和b人才能在同一时刻各打出一拳); 
		int f = 0;	 // 只有从0时刻找到第一个同时打出一拳的时刻,这是一个时间周期,所以就找最小公倍数;、
					//而在这个时间周期中,你能打出的拳数就是m:n为最简时,m+n拳,
					// 举个例子就是当a打出 2拳时,b能打 6拳,那么当a 打出1拳时,b就能打3拳,这时也就是最简了,这就是拳数的周期; 
		if(n>m)
		{
			f = 1;
			k1 = m; 
			k2 = n;
		}
		else 
		{
			k1 = n;
			k2 = m;
		}
		i = k1;     // k1为m,n中小的那个;k2为较大的那个;  
		long long  num1 = 1, num2 = 1,num = 1; //在i秒时,num1 记录k1 的打了多少拳,num2 为k2打了多少拳,num为总拳数 
		while(i<=tt) 
		{
			if(i%k1==0)
			{
				if(k2*num2<i)
					i = k2*num2;  // 挺重要的; 
				else 
				{
					num1++;
					book[num] = k1;
					num++;
					i+=k1;
				}
			}
			else if(i%k2==0)
			{
				if(k1*num1<i)
					i = k1*num1;  //挺重要; 
				else 
				{
					num2++;
					book[num] = k2;
					num++;
					if(i+k2 > num1*k1)   //最重要 若 x=2,y=6;若不加这一步,就把i=4给隔过去了; 
					{					 // 比赛中我就是没加这一步一直不过; 
						i = num1*k1;
					}
					else i+=k2;
				}
			}		
		} 
		for(j=0;j<k;j++)
		{
			scanf("%lld",&pp);
			pp = pp%kk;
			if(pp==0||pp==kk-1)
			{
				printf("Both\n");
				continue;
			}
			if(f)
			{
				if(book[pp]==k1)
					printf("Vova\n");
				else printf("Vanya\n");
			}
			else
			{
				if(book[pp]==k1)
					printf("Vanya\n");
				else printf("Vova\n");
			}
		}
	}
	return 0;	
}

思路二:

就是用二分的思想写,一直二分时间,定义一个很大的时间了 l = 0, r  = 很大的时间 

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
long long tt = 1;

void init()
{
	tt<<=50;
	//printf("tt==%lld\n",tt); 
}

int main()
{ 	init();
	long long i,j,n,x,y,kk;
	while(~scanf("%lld%lld%lld",&n,&x,&y))
	{
		for(i=0;i<n;i++)
		{
			scanf("%lld",&kk);
			long long ans,mid, l = 0,r = tt;
			while(l<=r)
			{
				mid = (l+r)/2;
				if(mid/x+mid/y>=kk)    // 这个二分法这用的挺好>=号,把mid 赋值给ans; 
				{			
					ans = mid;	  // 这个等于号的作用就是,找到恰好 打了kk下的时间,
					r = mid -1; 	//时间少一刻钟也就不满足了,所以上一刻钟就是恰好满足,也就是一定能对x取余等于0或者对y取余等于0; 
				}					// 一定要学会这样的二分法; 
				else l = mid + 1;
			}
			if(ans%x==0&&ans%y==0)
				printf("Both\n");
			else if(ans%x==0)
				printf("Vova\n");
			else if(ans%y==0)
				printf("Vanya\n");
		}
	}
	return 0;
} 



  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
CodeForces - 616D是一个关于一个序列中最长的第k好子段的起始位置和结束位置的问。给定一个长度为n的序列和一个整数k,需要一个子段,该子段中不超过k个不同的数字。目要求输出这个序列最长的第k好子段的起始位置和终止位置。 解决这个问的方法有两种。第一种方法是使用尺取算法,通过维护一个滑动窗口来记录\[l,r\]中不同数的个数。每次如果这个数小于k,就将r向右移动一位;如果已经大于k,则将l向右移动一位,直到个数不大于k。每次更新完r之后,判断r-l+1是否比已有答案更优来更新答案。这种方法的时间复杂度为O(n)。 第二种方法是使用枚举r和双指针的方法。通过维护一个最小的l,满足\[l,r\]最多只有k种数。使用一个map来判断数的种类。遍历序列,如果当前数字在map中不存在,则将种类数sum加一;如果sum大于k,则将l向右移动一位,直到sum不大于k。每次更新完r之后,判断i-l+1是否大于等于y-x+1来更新答案。这种方法的时间复杂度为O(n)。 以上是两种解决CodeForces - 616D问的方法。具体的代码实现可以参考引用\[1\]和引用\[2\]中的代码。 #### 引用[.reference_title] - *1* [CodeForces 616 D. Longest k-Good Segment(尺取)](https://blog.csdn.net/V5ZSQ/article/details/50750827)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Codeforces616 D. Longest k-Good Segment(双指针+map)](https://blog.csdn.net/weixin_44178736/article/details/114328999)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^koosearch_v1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值