前面的碎碎念:
报名前看到比赛难度宣称DIV2 A-C,总担心出题人会不会虚晃一枪,把菜逼的我锤懵了。做完后感觉难度确实算是牛客比赛里偏简单的,挺适合我这种新手练习。
题解部分:
A-Maximize The Beautiful Value
首先我们算出原序列的F(n)值,然后题目要求我们把序列中某个元素前移k格,假设这个元素移动前的位置为i,那么移动后这个元素的位置就变成了i-k,很显然这个值前移导致原序列的F(n)值减少了kai。除此之外,原序列i-k到i-1位置的元素全部后移一格,那么这些元素后移导致原序列的F(n)值增加了a[i-k]+a[i-k+1]+...+a[i-1],这个和可以利用前缀和预处理快速求解。因此,设原序列F(n)的值为s,设S数组为前缀和数组,那么将i位置元素前移k格之后的F[n]值即为:s-a[i]k+S[i-1]-S[i-k-1]。
我们从k+1处往后扫描整个数组,计算每个元素前移k格后的F[n]值,令ans保存前移后最大的F[n]值即可。
时间复杂度:O(Tn)。
代码部分:
#include<bits/stdc++.h> using namespace std; long long a[100005],S[100005]={0},s,ans; int main() { int i,n,k,T; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&k),ans=s=0; for(i=1;i<=n;i++) { scanf("%lld",&a[i]); s+=i*a[i],S[i]=S[i-1]+a[i]; } for(i=k+1;i<=n;i++)ans=max(ans,s-a[i]*k+S[i-1]-S[i-k-1]); printf("%lld\n",ans); } return 0; }
B-身体训练
因为题目说了每种顺序都是等概率的,那么某个人从最后面跑到最前面,他是第一个跑/第二个跑/.../第n个跑也都是等概率的。那么我们可以算出每个人从最后面跑到最前面,是第一个跑/第二个跑/.../第n个跑所消耗的时间。我们把这些时间全部求和,最后除以n即是答案。
至于计算时间,路程为nu,相对速度为c[i]-(j-1)d[i]-v。用路程除以速度即可得到时间。
时间复杂度:O(n^2)。
代码部分:
#include<bits/stdc++.h> using namespace std; int main() { int i,j,n; double ans=0,v,u,c[1005],d[1005]; scanf("%d%lf%lf",&n,&v,&u); for(i=1;i<=n;i++)scanf("%lf",&c[i]); for(i=1;i<=n;i++)scanf("%lf",&d[i]); for(i=1;i<=n;i++) for(j=1;j<=n;j++)ans+=n*u/(c[i]-(j-1)*d[i]-v); printf("%.3lf\n",ans/n); return 0; }
C-Borrow Classroom
判断dis(A,1)与dis(B,C)+dis(C,1)的大小:
若dis(A,1)<dis(B,C)+dis(C,1),那么老师总能更快一步到1号节点,然后在小C到达1号节点的子节点时,下一秒在路上拦截他,输出YES。
若dis(A,1)>dis(B,C)+dis(C,1),那么小C总能更快一步到达1号节点,老师拦截失败,输出NO。
若dis(A,1)=dis(B,C)+dis(C,1),需要进一步判断A和C的最近公共祖先。若A和C的最近公共祖先不为1,那么老师和小C可以在非1号节点的某节点碰面从而拦截成功,输出YES。若A和C的最近公共祖先为1,那么老师和小C只能在1号节点碰面,根据题意这样算作拦截失败,输出NO。
计算dis的话,算出所有节点的深度值,然后对于任意两个节点i和j,有dis(i,j)=dep[i]+dep[j]-2dep[lca(i,j)],其中lca为最近公共祖先。
时间复杂度:O(nlog(n)+qlog(n))。
代码部分:
#include<bits/stdc++.h> using namespace std; vector<int>R[100005]; int Dep[100005],F[100005][20],t; void DFS(int x,int fa) { int i,j,k; for(i=0;i<R[x].size();i++) { j=R[x][i]; if(j==fa)continue; Dep[j]=Dep[x]+1,F[j][0]=x; for(k=1;k<=t;k++)F[j][k]=F[F[j][k-1]][k-1]; DFS(j,x); } } int lca(int x,int y) { int i; if(Dep[x]>Dep[y])swap(x,y); for(i=t;i>=0;i--)if(Dep[F[y][i]]>=Dep[x])y=F[y][i]; if(y==x)return x; for(i=t;i>=0;i--)if(F[y][i]!=F[x][i])y=F[y][i],x=F[x][i]; return F[x][0]; } int main() { int i,j,k,n,q,T,ans1,ans2; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&q),t=(int)(log(n)/log(2))+1; for(i=1;i<=n;i++)R[i].clear(); for(i=0;i<n-1;i++) { scanf("%d%d",&j,&k); R[j].push_back(k),R[k].push_back(j); } Dep[1]=0,DFS(1,0); while(q--) { scanf("%d%d%d",&i,&j,&k); ans1=Dep[k]+Dep[k]+Dep[j]-2*Dep[lca(j,k)],ans2=Dep[i]; if(ans1>ans2)printf("YES\n"); else if(ans1==ans2) { if(lca(i,k)==1)printf("NO\n"); else printf("YES\n"); } else printf("NO\n"); } } return 0; }
D-景区路线规划
待补...
E-幸运数字Ⅱ
首先利用DFS构造长度为1到9的幸运数字,那么就有2+2^2+2^3+..+2^9=1022个幸运数字,再加上一个长度为10的4444444444,总共也就1023个幸运数字。我们对这些数字排个序,每个幸运数字之间就形成了一个块,我们还可以将其再简单的分为三部分,前端:l到大于等于l的第一个幸运数字,中间部分,尾部:小于r且离r最近的幸运数字到r。我们对这三个部分进行求和即可,注意l和r同属于一个块内的情况。
时间复杂度:O(1023log(1023))。
代码部分:
#include<bits/stdc++.h> using namespace std; long long n=0,lucky[2005]; void DFS(int L,int s) { if(L==10)return; if(s)lucky[n++]=s; DFS(L+1,s*10+4); DFS(L+1,s*10+7); } int main() { long long i,a,b,l,r,ans=0; DFS(0,0),lucky[n++]=4444444444,sort(lucky,lucky+n); scanf("%lld%lld",&l,&r); a=lower_bound(lucky,lucky+n,l)-lucky; b=lower_bound(lucky,lucky+n,r)-lucky; if(a==b)ans=(r-l+1)*lucky[a]; else { ans=(lucky[a]-l+1)*lucky[a]+(r-lucky[b-1])*lucky[b]; for(i=a+1;i<b;i++)ans+=(lucky[i]-lucky[i-1])*lucky[i]; } printf("%lld\n",ans); return 0; }
若有疏漏之处,还望大佬轻喷。