前言

今天又是颓废的一天,被大佬拉去跟他一起做牛客网的题,QAQ…那我会点啥嘛,就只能替大佬写两道水题了···


A. 牛牛战队的比赛地

题意

由于牛牛战队经常要外出比赛,因此在全国各地建立了很多训练基地,每一个基地都有一个坐标 $(x,y)$ 。 这周末,牛牛队又要出去比赛了,各个比赛的赛点都在 $x$ 轴上。牛牛战队为了方便比赛,想找一个到达训练基地最大距离最小的地方作为比赛地。请你求出选择的比赛地距离各训练基地最大距离的最小值。

思路

这个题首先一看到这种什么最大的最小,第一直觉就是二分。首先我们想一下应该二分什么,肯定先想的是枚举 $x$ 轴上的点,但是这样就会有个问题,二分要用的话必须是单调的,那么我们不能够确定越往右或者越往左,他们的这个值是单调的。因此我们可以用三分,一直向单峰逼近,最终寻找到那个极值点。(说实话这是我第一次接触到三分法,我太菜了。)

代码实现

#include<cstdio>
#include<cstring>
#include<iostream>
#include<cmath>
#include<algorithm>

using namespace std;

const int maxn=100005;
struct node
{
	int x,y;
}p[maxn]; //point
int n;
const double eps=1e-6;
double lmid,rmid,lans,rans;
double check(double x)
{
	double ans=0;
	for(int i=1;i<=n;i++)
	{
		double dis=(p[i].x-x)*(p[i].x-x)+p[i].y*p[i].y;
		ans=max(ans,dis);
	}
	return ans;
}
int main()
{
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d%d",&p[i].x,&p[i].y);
	}
	double l=-10000,r=10000;
	double ans=9999999999;
	while(r-l>=eps)
	{
		lmid=(r+l)/2;
		rmid=(r+lmid)/2;
		lans=check(lmid);
		rans=check(rmid);
		if(lans<rans)
		{
			ans=min(ans,lans);
			r=rmid;
		}
		else 
		{
			ans=min(ans,rans);
			l=lmid;
		}
	}
	printf("%lf",sqrt(ans));
	return 0;
} 

B. 牛牛与牛妹的约会

题意

你想从 $(a,0)$ 点到 $(b,0)$ 点,你可以除了可以以 $1m/s$ 的速度奔跑,还可以用1秒的时间来引导闪现,这将使你从 $(x,0)$ 点闪现到 $(\sqrt[3]{x},0)$ 点,问最少需要多长时间到达 $(b,0)$ 点。$(Ps:a,b \in[-10^6,10^6])$

思路

一道贪心的题目,当闪现所能贡献的距离大于 $1m$ ,那么我就选择用闪现,不然就直接奔跑。那么我们可以用距离的变化来体现闪现贡献的距离,一直用闪现到不能用之后,就直接加上最后剩下的距离即可。注意pow这个函数有点坑?如果底数是负数并且指数不是整数的话好像会返回很奇怪的值···(跟大佬调了好长时间都卡在这了)

代码实现

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<algorithm>

using namespace std;

int t,x,y;
double ans,a,b;
double dis,cdis;
int main(void)
{
	scanf("%d",&t);
	while(t--)
	{
		scanf("%d%d",&x,&y);
		a =(double)x;
		b=(double)y;
		dis = abs(a-b);
		if(a<0)
		{
			cdis=abs(-pow(-a,1.0/3.0)-b);
		}
		else cdis = abs(pow(a,1.0/3.0)-b);
		ans = 0;
		while(dis-cdis>1.0)
		{
			if(a<0)
			{
				a=-pow(-a,1.0/3.0);
			}
			else a=pow(a,1.0/3.0);
			ans+=1;
			dis = cdis;
			if(a<0)
			{
				cdis = abs(-pow(-a,1.0/3.0)-b);
			}
			else cdis = abs(pow(a,1.0/3.0)-b);
		}
		ans+=dis;
		printf("%.9lf\n",ans);
	}
	return 0;
}

C. 碎碎念

题意

大佬豪和弱鸡战合作做题,如果大佬豪AC掉题目,那么弱鸡战会说 “宁好强啊!”,如果大佬豪WA掉了题目,那么弱鸡战会嘲讽大佬豪 $k$ 句 “宁好弱啊!” 。我们规定大佬豪提交只有AC和WA两种状态。因为大佬豪非常的强,如果一道题他WA掉了一发,那么他的下一发一定会AC。如果已知最终弱鸡战嘲讽了 $x$ 句,那么很明显可以对应很多的提交序列。现在想问你如果弱鸡战嘲讽数在 $[l,r]$ 这个区间,一共会有多少种提交序列。答案对 $1e9+7$ 取模。

思路

首先原始题面不是这样,我把名字改了一下,QAQ… QAQ刷了这么多天的dp好像终于有点作用了,我终于看出来这是一道dp题了,还找对了他们的状态,不过转移方程却写错了。那么首先我们可以用 $f[i]$ 来表示,如果说了 $i$ 句话,那么一共有多少种可能的序列,但是这样的话我们发现没法确保上文上的如果WA掉了,下一发一定是AC。 所以我们可以考虑加一维状态来表示是通过哪种提交状态到达第 $i$ 句话的,也就是写成 $dp[0/1][i]$ 这个状态,$dp[0][i]$ 代表是从 $i-1$ 句话直接AC转移过来的,$dp[1][i]$ 是从 $i-k$ 句话通过WA转移过来的。所以这样的话转移方程就可以写出来了。

  • $dp[0][i] = dp[0][i-1]+dp[1][i-1]$ (可以从WA和AC转移过来)

  • $dp[1][i]=dp[0][i-k]$ (只能从第 $i-k$ 状态是AC的时候转移,不能连续两次WA)

因为最终是一个区间查询,那么我们可以用前缀和来优化。

代码实现

#include<cstdio>
#include<cmath>
#include<iostream>
#include<cstring>
#include<algorithm>

using namespace std;

const int maxn=100005;
int k,q;
int l,r;
int mod =1e9+7;
int dp[2][maxn];
int sum[maxn];
int ans[maxn];
int main()
{
	scanf("%d%d",&k,&q);
	dp[0][0]=1;
	for(int i=1;i<=100000;i++)
	{
		dp[0][i]=dp[0][i-1]+dp[1][i-1];
		dp[0][i]%=mod;
		if(i>=k)
		{
			dp[1][i]=dp[0][i-k];
			dp[1][i]%=mod;
		}
		ans[i]=dp[0][i]+dp[1][i];
		ans[i]%=mod; 
		sum[i]=sum[i-1]+ans[i];
		sum[i]%=mod;
	}
	for(int i=1;i<=q;i++)
	{
		scanf("%d%d",&l,&r);
		printf("%d\n",(sum[r]-sum[l-1]+mod)%mod);
	}
	return 0;
}

D. 牛牛战队的秀场

题意

在半径为 $r$ 的圆内有一个正接 $n$ 边形,随便选取一个顶点编号为 $1$ ,顺时针编号为 $2\sim n$ ,规定只能沿多边形边走,问从顶点 $i$ 到顶点 $j$ 最短路径为多少。

思路

很显然只有两条路可以走,我们只需要算出正多边形的每条边的边长,然后比较两条路径的大小,哪一个短就走哪一个就行,不过如果用了cos() 函数记得特判一下 $n=4$ 的情况,不然会发生错误。

代码实现

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#include<iostream>

using namespace std;

int n,ri;
double r;
int i,j;
double pi = 3.1415926535898;
int main()
{
	scanf("%d%d",&n,&ri);
	scanf("%d%d",&i,&j);
	double k=(double)2*pi/(double)n;
	double s;
	r=(double)ri;
	if(n==4)
	{
		s=sqrt(2*r*r);
	}
	else s=sqrt((double)2*r*r-2.0*r*r*cos(k));
	int p=abs(i-j);
	if(p>n/2)
	{
		printf("%lf",s*(n-p));
	}
	else printf("%lf",s*p);
	return 0;
}