【牛客小白月赛6】ABCDHJ题解

比赛链接:

https://www.nowcoder.com/acm/contest/136#question

总体情况:在比赛过程中,完成了ABDH题,比赛结束后,补充完成了CJ两题。

 

A题:简单模拟题,重点在于找全条件进行判断

题目描述:

HtBest有一条可爱的小鲲,HtBest想和与小鲲比赛游泳,我们可以把游泳池看成一个圆环,两人从起点游一圈回到起点即可完成比赛。两人在距离小于k时(距离指的是在环上的距离,而非直线距离),他们会互相监督对方有没有全速向前游,如果发现有一方没有全速向前游,则视为作弊,聪明的HtBest为了省力(更为了赢得比赛),可以选择在两者相距超过k的时候,立马掉头反向游以更快到达起点,HtBest想让你求出小鲲最少比HtBest提前多长时间完成比赛。(若HtBest比小鲲先完成比赛,输出负数)

说明:小鲲不会作弊。

输入描述:

一行,包含四个正整数,两两之间用空格分开:L(游泳池周长)、k(互相监督距离)、a(小鲲速度)、b(HtBest速度)。 所有输入数据均不超过1e9。

输出描述:

一行,包含一个数,表示小鲲最少比HtBest提前多长时间完成比赛。(保留两位小数)。

分析:

重点要分析完全部的可能性

1.如果HtBest速度比小鲲大,由于小鲲不会作弊,所以就是正常的游

2.如果HtBest速度比小鲲小,那么就进行判断,有没有可能在游的过程中,距离超过k,如果没有,那也是正常游;如果有超过k,那就说明HtBest要可能作弊了。

3.在HtBest要可能作弊情况下,如果此时HtBest已经游完了L/2,才能开始作弊,那么作弊反倒时间更长,没必要。那如果还没游到L/2的时候,那就可以作弊了,但是此时,由于作弊的时候,往回游,也有可能和小鲲的距离小于k,所以还要进行判断,如果作弊的回游的时候,和小鲲的距离可能小于k,那就说明作弊会被发现,那也只能好好的往前游;如果作弊往回游的时候,不会被发现,那才能真正的作弊

AC代码:

#include<bits/stdc++.h>
using namespace std;
const double EPS = 1e-3;

int main()
{
	int L,k,a,b;
	scanf("%d %d %d %d",&L,&k,&a,&b);
	double kunTime = L/1.0/a;
	double htTime;
	double falseTime;
	double len = kunTime*b;
	if(b >= a)
	{
		htTime = L/1.0/b;
	}
	else
	{
	
		if((L-len)>(k+EPS))  // 正常游的时候,会超过k,说明可能可以作弊 
		{
			falseTime = k/1.0/(a-b);
			if(falseTime*b > L/2+EPS)  // 超过一半 ,没必要作弊 
			{
				
				htTime = L/1.0/b;
			}
			else
			{
				if(L-falseTime*2*a+EPS < k) // 如果反向跑,距离小于k的时候,那也不能作弊 
				{
					htTime = L/1.0/b;
				}
				else
					htTime = 2*falseTime;   // 最后终于满足前面的作弊条件了,可以作弊了 
			}
		//htTime = 2*falseTime;
		}
		else
		{
			htTime = L/1.0/b;
		}
	}
	double ans = htTime-kunTime;
	printf("%.2lf\n",ans);
	
	return 0;
}

 

B题:简单题,模拟上升下降过程,如果都有,那就是翻越次数+1

题目描述:

HtBest的小鲲长大变成了大鹏,大鹏在天际翱翔,看到了一片绵延的山脉,每座山都有自己的高度,大鹏想穿过这片山脉。由于他只能紧贴地面飞行,他想知道他一共要翻越几次大山(上升->平飞->下降,算一次,其中平飞可以没有),初始时,大鹏在山脉的左端。

输入描述:

第一行一个正整数n,表示山脉被分为n段。

第二行有n个正整数ai两两之间用空格分开,ai表示山脉第i段的高度。

输出描述:

一行,包含一个正整数,表示大鹏需要翻越几次大山。

分析:

上升,就是当前比上一个高,那就是上升

下降,就是当前比上一个低,那就是下降

同时,我们要求下降的前提,要求有上升

最后当有上升和下降的时候,翻越次数就++,即答案就++

AC代码:

#include<bits/stdc++.h>
using namespace std;

int a[1000005];
int ans = 0;

int main()
{
	int n;
	scanf("%d",&n);
	bool up = 0;
	bool down = 0;
	for(int i = 0;i < n;i++)
	{
		scanf("%d",&a[i]);	
	}
	//printf("110\n");
	for(int i = 1;i < n;i++)
	{
		if(a[i] > a[i-1])
			up = 1;
		if(up && a[i]<a[i-1])
			down = 1;
		
		if(up==1&&down==1)
		{
			up = 0;
			down = 0;
			ans++;
		}
	}
	printf("%d\n",ans);
	
	return 0;
}

 

C题:计算树的从一个叶节点到另一个叶节点的最大长度,这就是一条链,要经过最多的节点

题目描述:

桃花长在桃树上,树的每个节点有一个桃花,调皮的HtBest想摘尽可能多的桃花。HtBest有一个魔法棒,摘到树上任意一条链上的所有桃花,由于HtBest法力有限,只能使用一次魔法棒,请求出Htbest最多可以摘到多少个桃花。

输入描述:

第一行有一个正整数n,表示桃树的节点个数。

接下来n-1行,第i行两个正整数ai,bi ,表示桃树上的节点ai,bi之间有一条边。

输出描述:

第一行一个整数,表示HtBest使用一次魔法棒最多可以摘到多少桃花。

分析:

题目的意思,就是要求我们求从一个叶节点到另一个叶节点的经过最多节点数的链。

那么我们的想法,通过dfs,先从某个点开始,找到最深的叶节点,那么记录这个叶节点的位置,然后再从这个叶节点dfs出发,找到和这个叶节点深度最大的另一个叶节点,其中的深度就是我们要的答案。

所以我们要用到深度优先搜索DFS

dfs三个参数,第一个记录当前节点,第二个记录父节点,第三个记录当前的深度,只要这个深度比上一个的大,那就说明这个更深了,就要进行答案变为此时深度,且记录下这个当前节点的id。然后再对当前节点以下的子节点进行dfs,直到所有的节点都计算过为止。

AC代码:

/*
	主要就是一条链上的最多的节点数
	那么先从1找到最下面的,然后从这个最深的
	找到另一边最深的,这样子就是最长的链子 
*/

#include<cstdio>
#include<vector>
using namespace std;

void read(int &x) {
    x = 0;
    char c = getchar();
    while (c < '0' || c > '9')c = getchar();
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
}

vector<int> g[1000002];
int ans,id;

void dfs(int u,int f,int dep)
{
	if(dep > ans)
	{
		ans = dep;
		id = u;
	}
	for(int i = 0;i < g[u].size();i++)
	{
		int s = g[u][i];
		if(s == f)
			continue;
		dfs(s,u,dep+1);
	}
	
	
}

int main()
{
	int n;
	read(n);
	int u,v;
	// 无向图 
	for(int i = 0;i < n-1;i++)
	{
		read(u);read(v);
		g[u].push_back(v);
		g[v].push_back(u);
	} 
	ans = 0;
	id = 1;
	dfs(1,1,1);
	ans = 0;
	dfs(id,id,1);
	printf("%d\n",ans);
	
	return 0;
}

 

D题:字符串题目,用一个数组记录,打表

题目描述:

    WHZ送给了HtBest一个“字符串丝带”,这条丝带由n个小写字母按照一定的顺序排列组成,HtBest收到新礼物后有许多问题,类似“第i个位置的字母在前i个位置中出现了几次?”,HtBest很希望知道答案,于是求助你帮忙解答。

输入描述:

第一行有2个正整数n,m,分别表示丝带长度和问题个数。

第二行,有n个小写字母,第i个表示丝带第i位的小写字母。

接下来有m行,每行一个正整数 ,表示HtBest的一个问题。

输出描述:

共m行,对于每个问题,给出答案。

分析:

弄两个一维数组,

一个数组用于存放当前字母出现的次数,如果再出现,那就++

一个数组用于记录第i个的输出,也就是当前字母的出现次数就是对应这个数组第i个的值

不要用二维数组,会超内存,而且在第i位的这个字母,只有这个字母有作用,其他的没用,相当于浪费了。

AC代码:

#include<bits/stdc++.h>
using namespace std;

int cnt[1000005];
int num[26];
char str[1000005];

int main()
{
	int n,m;
	scanf("%d %d",&n,&m);
	//getchar();
	char c;
	for(int i = 1;i <= n;i++)
	{
		scanf(" %c",&str[i]);
		cnt[i] = ++num[str[i]-'a'];
	}
	int q;
	for(int i = 1;i <= m;i++)
	{
		scanf("%d",&q);
		
		printf("%d\n",cnt[q]);
	}
	
	return 0;
} 

 

H题:最小生成树

题目描述:

胡队长带领HA实验的战士们玩真人CS,真人CS的地图由一些据点组成,现在胡队长已经占领了n个据点,为了方便,将他们编号为1-n,为了隐蔽,胡队长命令战士们在每个据点出挖一个坑,让战士们躲在坑里。由于需要在任意两个点之间传递信息,两个坑之间必须挖出至少一条通路,而挖沟是一件很麻烦的差事,所以胡队长希望挖出数量尽可能少的沟,使得任意两个据点之间有至少一条通路,顺便,尽可能的∑d[i][j]使最小(其中d[i][j]为据点i到j的距离)。

输入描述:

第一行有2个正整数n,m,m表示可供挖的沟数。

接下来m行,每行3个数a,b,v,每行描述一条可供挖的沟,该沟可以使a与b连通,长度为v。

输出描述:

输出一行,一个正整数,表示要使得任意两个据点之间有一条通路,至少需要挖长的沟。(数据保证有解)

分析:

其实就是最小生成树,具体可以参考HDU1863畅通工程的题目,说白了就是模板题

主要要弄懂怎么解决最小生成树的问题。

AC代码:

/**
"不要怕,其实很简单,你一定可以的"

kruskal算法:(并查集用于判断两节点是否已经连通,并查集广泛用于图论)

先把所有路线按权值排序,然后从小到大遍历每一条路线,
如果路线的两端尚没有连通,就把该路线连通,并更新已连通路线的长度和数目,
最后如果”已连通路线数目=端点数-1”则已连通路线长度为最小生成树。
*/


#include<stdio.h>
#include<stdlib.h>
int set[100005],flag;
struct road
{
    int a;
    int b;
    int value;
}s[1000005];
int cmp(const void*a,const void*b)
{
    return (*(struct road*)a).value-(*(struct road*)b).value;
}
int Findset(int x)
{
    if(x!=set[x])
        set[x]=Findset(set[x]);
    return set[x];
}
void Unionset(int a,int b)
{
    int x=Findset(a);
    int y=Findset(b);
    if(x==y)
        return;
    set[y]=x;
    flag=1;
}
int main()
{
    int n,m,i,t,sum;
    // n是路,m是村(<=100) 
    scanf("%d %d",&m,&n);
    
    for(i=1;i<=n;i++)
        set[i]=i;
    sum=0;t=0;
    for(i=0;i<n;i++)
        scanf("%d%d%d",&s[i].a,&s[i].b,&s[i].value);
    qsort(s,n,sizeof(s[0]),cmp);
    for(i=0;i<n;i++)
    {
        flag=0;
        Unionset(s[i].a,s[i].b);///可以加入这条边吗?
        if(flag)
        {
            sum+=s[i].value;///最小生成树的长度
            t++;///节点+1
        }
    }
    printf("%d\n",sum);

    
    return 0;
}

 

J题:数学公式推导题

题目描述:

WHZ有很多铸造成三角形的洋灰块,他想把这些洋灰三角按照一定的规律放到摆成一排的n个格子里,其中第i个格子放入的洋灰三角数量是前一个格子的k倍再多p个,特殊地,第一个格子里放1个。
WHZ想知道把这n个格子铺满需要多少洋灰三角。

输入描述:

第一行有3个正整数n,k,p。

输出描述:

输出一行,一个正整数,表示按照要求铺满n个格子需要多少洋灰三角,由于输出数据过大,你只需要输出答案模1000000007(1e9+7)后的结果即可。

分析:

推导过程如下图所示:

 

然后分成了三种情况,最后一种情况除法的求模,我们往往利用“逆元”变为乘法的求模,具体可以参考我的另一篇博客,里面就讲解了除法的求模应该怎么做(利用“逆元”):常见的几种求模运算(mod)

AC代码:

// * / % 的优先级一致,所以从左到右 

#include<bits/stdc++.h>
using namespace std;
#define LL long long
const LL MOD = 1e9+7;

LL qPow(LL x,LL y)
{
	LL ans = 1;
	while(y)
	{
		if(y&1)
		{
			ans *= x;
			if(ans > MOD)
				ans %= MOD;
		}	
		x *= x;
		if(x > MOD)
			x %= MOD;
		y >>= 1;
	}
	ans %= MOD;
	return ans;
}

int main()
{
	LL n,k,p;
	cin >> n >> k >> p;
	LL ans;
	if(n==1)
	{
		ans = 1;
	}
	else
	{
		if(k==1)
			ans = (n*(n-1)/2%MOD*p+n)%MOD;
		else
		{
			ans = qPow((k-1)*(k-1),MOD-2)*(qPow(k,n)*(k-1+p)%MOD-(k-1)*(n*p+1)%MOD-p+MOD)%MOD;
		}
		
	}
	cout << ans << endl;
	
	return 0;
}

 

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值