牛客算法周周练1补题

阿楚姐骗我们这场只有div2A~C难度
比赛传送门
题解传送门,目前似乎没有官方题解

A.Maximize The Beautiful Value

题意

给你长度为 n n n的数组 a 1 … a n a_1\dots a_n a1an,和一个整数 k k k,你必须从数组中选一个数,并将它向前移动 k k k位,使得 F ( n ) = ∑ i = 1 n i × a i F(n)=\sum_{i=1}^ni\times a_i F(n)=i=1ni×ai的值最大。

思路

前缀和+枚举。
先在读入时预处理出前缀和,计算出原本的 a a a数组对应的 F ( n ) F(n) F(n),记为 M M M
当枚举到第 i i i位时,因为第 i i i位的移动使得前面的 k k k位向后移一位,而自身又向前移动了 k k k位,相对于 M M M产生的变化即为 − a i ∗ k + ∑ j = i − k i − 1 a j -a_i*k+\sum_{j=i-k}^{i-1}a_j aik+j=iki1aj,记录其中的最大值即为答案
时间复杂度 O ( n ) O(n) O(n)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
ll a[maxn],sum[maxn];
signed main()
{
	int t,n,k;
	scanf("%d",&t);
	while(t--)
	{
		ll ans=0,ma=0;
		scanf("%d%d",&n,&k);
		for(int i=1;i<=n;i++)
		{
			scanf("%lld",&a[i]);
			sum[i]=sum[i-1]+a[i];
			ma+=i*a[i];
		}
		for(int i=k+1;i<=n;i++)
			ans=max(ans,ma-a[i]*k+sum[i-1]-sum[i-k-1]);
		printf("%lld\n",ans);
	}
	return 0;
}

B.身体训练

题意

这道题原题题面很坑……建议不看自己猜题意
n n n个人排成一竖排以速度 v v v跑步,正常队形中每两个人间隔为 u u u,每个人有超车速度 c c c和衰减速度 d d d,当一个人在队伍末尾时,此人开始以速度 c − ( j − 1 ) × d c-(j-1)\times d c(j1)×d进行超车,这里的 j j j表示此人是第 j j j个开始超车的。
现在 n n n个人初始位置随机,求每个人都进行一次超车所用的时间期望。
坑点:当上一个人还在超车过程中,当前队伍末尾的人是不开始超车的;而当上一个人到达了新的排头,末尾的人立即开始进行超车。

思路

计算出每个人每种可能的出发次序达到队首的所用时间并求和,最后除以 n n n,即为答案。
时间复杂度 O ( n 2 ) O(n^2) O(n2)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int ll
const int maxn=1010,inf=0x3f3f3f3f,mod=1000000007;
double c[maxn],d[maxn];//初始速度,每轮衰减量
signed main()
{
	int n;
	double v,u,ans=0;
	cin>>n>>v>>u;//n个人,间隔u米,每个人速度均为v
	for(int i=1;i<=n;i++)
		cin>>c[i];
	for(int i=1;i<=n;i++)
		cin>>d[i];
	for(int i=1;i<=n;i++)
	{//第i个人以第j位出发所用时间
		for(int j=1;j<=n;j++)
		{//此人相对于队伍的速度
			ans+=n*u/(c[i]-(j-1)*d[i]-v);
		}
	}
	cout<<setiosflags(ios::fixed)<<setprecision(3)<<ans/n<<endl;
	return 0;
}

C. Borrow Classroom

题意

一棵 n n n个节点的树, 1 1 1为树的根节点, q q q个询问,每个询问三个数字 a , b , c a,b,c a,b,c,表示

  • 同学行走的路径为 b ⇒ c ⇒ 1 b\Rightarrow c\Rightarrow1 bc1,保证 c ≠ 1 c\neq 1 c=1
  • 老师初始位置为 a a a
    询问老师是否能在同学到达 1 1 1节点前追上同学

思路

很明显这是个LCA的题,符合题意的情况有两种
d 1 d1 d1为老师到 1 1 1节点的路程, d 2 d2 d2为同学先到 c c c再到 1 1 1行走的路程。

  • 如果 d 1 < d 2 d1<d2 d1<d2,则说明老师可以先到 1 1 1节点,从而截住同学
  • 如果 d 1 = d 2 d1=d2 d1=d2,如果老师和节点 c c c关于 1 1 1的同一棵子树上,则说明老师和同学会在到达 1 1 1节点之前,即 l c a ( a , c ) lca(a,c) lca(a,c)相遇,否则无法在同学到达 1 1 1之前截住

注意:多组输入,不用 m e m s e t memset memset见祖宗
单组时间复杂度 O ( n + q l o g n ) O(n+qlogn) O(n+qlogn)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
void read(){}
template<typename T,typename... T2>inline void read(T &x,T2 &... oth) {
	x=0; int ch=getchar(),f=0;
	while(ch<'0'||ch>'9'){if (ch=='-') f=1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}
	if(f)x=-x;
	read(oth...);
}
const int maxl=30;
vector<int> G[maxn];//无权边,也可以选择链式前向星存图
int gene[maxn][maxl],depth[maxn],lg[maxn];
//int nodes[maxn];//子树结点的数量
void dfs(int x,int fa)
{
	depth[x]=depth[fa]+1;
	gene[x][0]=fa;
//	nodes[x]=1;
	for(int i=1;(1<<i)<=depth[x];i++)//倍增
		gene[x][i]=gene[gene[x][i-1]][i-1];
	for(int i=0;i<G[x].size();i++)
		if(G[x][i]!=fa)
		{
			dfs(G[x][i],x);//在dfs前后加语句可以求出许多有趣的东西
//			nodes[x]+=nodes[G[x][i]];
		}
}
int lca(int x,int y)
{
	if(depth[x]<depth[y])//保证x深度≥y
		swap(x,y);
	while(depth[x]>depth[y])//将x提到同一高度
		x=gene[x][lg[depth[x]-depth[y]-1]];
	if(x==y)//是同一个节点
		return x;
	for(int i=lg[depth[x]];i>=0;i--)
		if(gene[x][i]!=gene[y][i])
		{//二分思想,直到跳到LCA的下面一层
			x=gene[x][i];
			y=gene[y][i];
		}
	return gene[x][0];
}
int dist(int x,int y)//x节点到y结点的距离
{
	int tem=lca(x,y);
	return (int)(abs(depth[x]-depth[tem])+abs(depth[y]-depth[tem]));
}
void init(int s,int n)
{
	memset(gene,0,sizeof(gene));
	depth[s]=0;
	for(int i=1;i<=n;i++)//预处理出log2(i)+1的值
		lg[i]=lg[i-1]+((1<<(lg[i-1]+1))==i);//不要写错
	dfs(s,0);
}
signed main()
{
	std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0);
	#ifdef DEBUG
		freopen("input.in", "r", stdin);
	//	freopen("output.out", "w", stdout);
	#endif
	int t,n,u,v,q;
	read(t);
	while(t--)
	{
		read(n,q);
		for(int i=1;i<=n;i++)
			G[i].clear();
		for(int i=1;i<n;i++)
		{
			read(u,v);
			G[u].push_back(v);
			G[v].push_back(u);
		}
		init(1,n);
		int a,b,c;
		while(q--)
		{
			read(a,b,c);
			bool ok=0;
			int t=dist(a,1),sk=dist(b,c)+dist(c,1);
			if(t<sk||(t==sk&&lca(c,a)!=1))
				ok=1;
			printf("%s\n",(ok?"YES":"NO"));
		}
	}
	return 0;
}

E.幸运数字Ⅱ

题意

定义一个数字为幸运数字当且仅当它的所有数位都是4或者7。
比如47、744、4都是幸运数字而5、17、467都不是。
定义 n e x t ( x ) next(x) next(x)为大于等于 x x x的第一个幸运数字。
给定 l , r l,r lr,求出 n e x t ( l ) + n e x t ( l + 1 ) + . . . + n e x t ( r − 1 ) + n e x t ( r ) next(l) + next(l + 1) + ... + next(r - 1) + next(r) next(l)+next(l+1)+...+next(r1)+next(r)

思路

使用dfs枚举构造出所有幸运数字,排序后在数组中二分查找当前数字的幸运数字
时间复杂度估计为 O ( 2 10 + 10 × 2 10 + 10 × l o g 10 r − l ) O(2^{10}+10\times 2^{10}+10\times log_{10}^{r-l}) O(210+10×210+10×log10rl)

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
//#define int ll
const int maxn=2e5+10,inf=0x3f3f3f3f,mod=1000000007;
vector<ll>luck;
void dfs(ll x)
{
	if(x>5e9)
		return;
	luck.push_back(x);
	dfs(x*10+4);
	dfs(x*10+7);
}
signed main()
{
	dfs(0);
	sort(luck.begin(),luck.end());
	ll l,r,ans=0;
	cin>>l>>r;
	while(l<=r)
	{
		ll nex=*lower_bound(luck.begin(),luck.end(),l);
		if(nex>=r)
		{
			ans+=(r-l+1)*nex;
			break;
		}
		ans+=(nex-l+1)*nex;
		l=nex+1;
	}
	cout<<ans<<endl;
	return 0;
}

在这里插入图片描述

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值