牛客练习赛56 题解

1|0前言


菜鸡OIer没有写出F题来qwq,想看F题题解的dalao们可以离开了。

2|0A 小蒟和他的乐谱


2|1题目


小蒟上音乐课的时候,老师说宫商角徵羽(分别对应C大调的do,re,mi,sol,la)五个音是乐音,它们和它们升降任意个八度的得到音是好听的音(即高音do、低音mi等也是好听的音),用好听的音谱的曲会很好听。
小蒟觉得他的老师说得对,于是他打开了一本乐谱,随便找了一首曲子,他想知道这首曲子的好听程度。
小蒟太蒻了,善良的你不得不帮助他。
注:
一首曲子是一个整数序列,数字表示音高,17分别代表C大调的do,re,mi,fa,sol,la,si,8代表高音do(即1˙),0代表低音si,15代表1˙˙ ,-123表示很低很低的mi,以此类推。
曲子的好听程度定义为曲子中最长的全部由好听的音组成的子串的长度。
对于100%的数据,1n1,000,000,109Ai109

2|2思路


送分题。
直接判断(x%7+7)%7是不是1,2,3,5,6中的一个即可。

2|3代码


#include <queue> #include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=1000010; int n,m,b[N],a[N],cnt,ans; int main() { scanf("%d",&n); for (int i=1,p;i<=n;i++) { scanf("%d",&p); p=(p%7+7)%7; if (p==1||p==2||p==3||p==5||p==6) cnt++; else { ans=max(ans,cnt); cnt=0; } } printf("%d",ans); return 0; }

3|0B 小琛和他的学校


3|1题目


小琛是一所学校的校长。
他的学校有n个校区(编号1n),被n1条双向道路连接,呈树形结构。
i个校区共有Ai个学生。
i天早上,所有的学生会沿最短路走到第i个校区参加活动,晚上再原路返回。
一个人通过第j条通道一次(即一人次),需要小琛支付wj的维护费用。
小琛想知道第n天结束之后,对于每一条通道,他总共需要支付多少费用。
对于100%的数据,1n200,0001A[i]10,0001w[i]10,000

3|2思路


还是送分题。
维护一下以一为根时,每一个点的大小ss[x],以及每一个点为根的子树内有多少个人size[x]
然后对于每一个点x,它与它父节点fa的边就会有ss[x]×(sumsize[x])+(nss[x])×size[x]人次单向通过。
然后这条边的答案就是上式×2×dis(v,fa)

3|3代码


#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N=200010; int n,m,tot,head[N]; ll ans[N],sum,size[N],ss[N]; struct edge { int next,to,dis,id; }e[N*2]; void add(int from,int to,int dis,int x) { e[++tot].to=to; e[tot].id=x; e[tot].dis=dis; e[tot].next=head[from]; head[from]=tot; } void dfs1(int x,int fa) { ss[x]=1; for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa) { dfs1(v,x); size[x]+=size[v]; ss[x]+=ss[v]; } } } void dfs2(int x,int fa) { for (int i=head[x];~i;i=e[i].next) { int v=e[i].to; if (v!=fa) { ans[e[i].id]=1LL*size[v]*(n-ss[v])+1LL*(sum-size[v])*ss[v]; dfs2(v,x); } } } int main() { memset(head,-1,sizeof(head)); scanf("%d",&n); for (int i=1;i<=n;i++) { scanf("%lld",&size[i]); sum+=size[i]; } for (int i=1,x,y,z;i<n;i++) { scanf("%d%d%d",&x,&y,&z); add(x,y,z,i); add(y,x,z,i); } dfs1(1,0); dfs2(1,0); for (int i=1;i<n;i++) printf("%lld\n",ans[i]*e[i*2].dis*2LL); return 0; }

4|0C 小魂和他的数列


4|1题目


一天,小魂正和一个数列玩得不亦乐乎。
小魂的数列一共有n个元素,第i个数为Ai
他发现,这个数列的一些子序列中的元素是严格递增的。
他想知道,这个数列一共有多少个长度为K的子序列是严格递增的。
请你帮帮他,答案对998244353取模。
对于100%的数据,1n500,000,2K10,1Ai109

4|2思路


先离散化。
f[i][j]表示以(数字)i结尾的长度为j的严格递增子序列的个数。
那么枚举序列a,假设现在枚举到第k位,那么需要更新的只有f[a[k]][1m]
那么显然有

f[a[k]][p]=q=1q<a[k]f[q][p1]

这样我们就得到了一个O(n2k)的优秀算法,在ACM赛制下获得0pts的高分。
我们发现每次转移是一个前缀和,考虑用树状数组维护前缀和。
k棵树状数组,第i棵表示严格递增序列长度为i的数量。
那么q=1q<a[k]f[q][p1]=bit[p1].ask(a[k]1)
时间复杂度O(nklogn)

4|3代码


#include <cstdio> #include <cstring> #include <algorithm> using namespace std; const int N=500010,M=11,MOD=998244353; int n,m,a[N],b[N],f[N][M],cnt[N],cpy[M]; struct Bit { int c[N]; void add(int x,int val) { for (int i=x;i<=n;i+=i&-i) c[i]=(c[i]+val)%MOD; } int ask(int x) { int ans=0; for (int i=x;i;i-=i&-i) ans=(ans+c[i])%MOD; return ans; } }bit[M]; int main() { for (int i=0;i<=10;i++) memset(bit[i].c,0,sizeof(bit[i].c)); scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%d",&a[i]); b[i]=a[i]; } sort(b+1,b+1+n); int totel=unique(b+1,b+1+n)-b-1; for (int i=1;i<=n;i++) a[i]=lower_bound(b+1,b+1+totel,a[i])-b; for (int i=1;i<=n;i++) { cnt[a[i]]++; f[a[i]][1]=cnt[a[i]]; bit[1].add(a[i],1); for (int j=2;j<=m;j++) { cpy[j]=bit[j-1].ask(a[i]-1); f[a[i]][j]=(f[a[i]][j]+cpy[j])%MOD; } for (int j=1;j<=m;j++) bit[j].add(a[i],cpy[j]); } int ans=0; for (int i=1;i<=n;i++) ans=(ans+f[i][m])%MOD; printf("%d",ans); return 0; }

5|0D 小翔和泰拉瑞亚


5|1题目


小翔爱玩泰拉瑞亚 。
一天,他碰到了一幅地图。这幅地图可以分为n列,第i列的高度为Hi,他认为这个地图不好看,决定对它进行改造。
小翔又学会了m个魔法,实施第i个魔法可以使地图的第Li列到第Ri列每一列的高度减少Wi,每个魔法只能实施一次,魔法的区间可能相交或包含。
小翔认为,一幅地图中最高的一列与最低的一列的高度差越大,这幅地图就越美观。
小翔可以选择m个魔法中的任意一些魔法来实施,使得地图尽量美观。但是他不知道该如何选择魔法,于是他找到了你。请你求出所有可行方案中,高度差的最大值。
对于100%的数据,满足1n,m200000,109Hi109,1Wi109,1LiRin

5|2思路


先把所有操作进行,然后就相当于每次选择一个操作将其撤回,也就是将这个操作的区间加上这一个数。
如果我们已经知道一个位置x最终会是最大值了,那么我们肯定把所有包含x的区间撤回,如果最小值在这个区间,那么最大值与最小值只差不变,不影响结果;如果最小值不在这个区间,结果会变大。所以撤回所有x所在区间会最优。
但是我们并不知道最大值是谁,所以考虑枚举最大值。
我们发现,如果我们已经知道x为最大值时每一个位置的数值了,那么我们要推到位置x+1为最大值时,只需要把所有右端点为x的区间减去,左端点为x+1的区间加上。这样每一个修改总共只会便利两次。
所以先暴力求出位置1为最大值时的答案,然后把此时每一个位置的数值扔进一棵线段树内,然后每次区间修改,在所有答案中取一个最大值即可。
时间复杂度O(nlogn)

5|3代码


#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N=200010; int n,m,head,tail; ll ans,b[N],a[N],s[N]; struct node { int l,r,val; }q1[N],q2[N]; bool cmp1(node x,node y) { return x.l<y.l; } bool cmp2(node x,node y) { return x.r<y.r; } struct Treenode { int l,r; ll minn,lazy; }; struct TRee { Treenode tree[N*4]; void pushup(int x) { tree[x].minn=min(tree[x*2].minn,tree[x*2+1].minn); } void pushdown(int x) { if (tree[x].lazy) { tree[x*2].lazy+=tree[x].lazy; tree[x*2+1].lazy+=tree[x].lazy; tree[x*2].minn+=tree[x].lazy; tree[x*2+1].minn+=tree[x].lazy; tree[x].lazy=0; } } void build(int x,int l,int r) { tree[x].l=l; tree[x].r=r; if (l==r) { tree[x].minn=a[tree[x].l]; return; } int mid=(l+r)>>1; build(x*2,l,mid); build(x*2+1,mid+1,r); pushup(x); } void update(int x,int l,int r,int val) { if (tree[x].l==l && tree[x].r==r) { tree[x].minn+=val; tree[x].lazy+=val; return; } pushdown(x); int mid=(tree[x].l+tree[x].r)>>1; if (r<=mid) update(x*2,l,r,val); else if (l>mid) update(x*2+1,l,r,val); else update(x*2,l,mid,val),update(x*2+1,mid+1,r,val); pushup(x); } }Tree; int main() { scanf("%d%d",&n,&m); for (int i=1;i<=n;i++) { scanf("%lld",&a[i]); b[i]=a[i]; } for (int i=1;i<=m;i++) { scanf("%d%d%d",&q1[i].l,&q1[i].r,&q1[i].val); s[q1[i].l]+=1LL*q1[i].val; s[q1[i].r+1]-=1LL*q1[i].val; q2[i].l=q1[i].l; q2[i].r=q1[i].r; q2[i].val=q1[i].val; } for (int i=1;i<=n;i++) { s[i]+=s[i-1]; a[i]-=s[i]; } Tree.build(1,1,n); sort(q1+1,q1+1+m,cmp1); sort(q2+1,q2+1+m,cmp2); head=1; for (;q1[tail+1].l==1;tail++) Tree.update(1,q1[tail+1].l,q1[tail+1].r,q1[tail+1].val); ans=b[1]-Tree.tree[1].minn; for (int i=2;i<=n;i++) { for (;q1[tail+1].l==i;tail++) Tree.update(1,q1[tail+1].l,q1[tail+1].r,q1[tail+1].val); for (;q2[head].r==i-1;head++) Tree.update(1,q2[head].l,q2[head].r,-q2[head].val); ans=max(b[i]-Tree.tree[1].minn,ans); } printf("%lld",ans); return 0; }

6|0E 小雀和他的王国


6|1题目


年纪轻轻的小雀当上了国王。
小雀的王国中一共有n座城市(编号为1n),被m条双向的高速公路连接,任意两个城市之间都可以通过若干条高速公路互相到达。
但是在小雀的王国里,经常发生自然灾害。一次突发的自然灾害会随机破坏一条高速公路,并且有可能使得某两个城市之间无法到达彼此,这样整个王国就不能继续正常运转了。小雀为此很是苦恼。
于是小雀决定再修建一条高速公路,连接某两个城市,使得下一次突发自然灾害的时候,整个王国不能继续正常运转的概率最小。但是他不知道该如何选择新高速公路所连接的两个城市。
请你帮助他选择新建高速的方案,并求出新的高速公路建成之后,突发自然灾害时,整个王国不能继续正常运转的最小概率。答案对109+7取模。
对于100%的数据,2n,m200000

6|2思路


明显,当且仅当一条边是桥时,删去这条边才会把原图分成多个连通块。
所以我们先把这张图缩点,这样的一棵树中,删除任意一条边就会使原图不连通。
此时我们需要加入一条边,使得加入这条边后,这棵基环树的桥最少,也就是环上的点尽量多,其实就是缩点后的树的直径了。
用缩点后的图的点数减去树的直径,即为最终的桥的最少数量s
此时的答案即为sm+1,快速幂跑一波即可。
时间复杂度O(n)

6|3代码


#include <cstdio> #include <cstring> #include <algorithm> using namespace std; typedef long long ll; const int N=200010,MOD=1e9+7; int n,m,tot=1,cnt,maxlen,f[N],low[N],dfn[N],head[N],pos[N],U[N],V[N]; bool brg[N*2],vis[N]; struct edge { int next,to; }e[N*2]; void add(int from,int to) { e[++tot].to=to; e[tot].next=head[from]; head[from]=tot; } void tarjan(int x,int in) { dfn[x]=low[x]=++tot; for (int i=head[x];~i;i=e[i].next) { int y=e[i].to; if (!dfn[y]) { tarjan(y,i); low[x]=min(low[x],low[y]); if (low[y]>dfn[x]) { brg[i]=brg[i^1]=1; cnt++; } } else if (i!=(in^1)) low[x]=min(low[x],dfn[y]); } } void dfs(int x,int fa,int id) { if (vis[x]) return; pos[x]=id; vis[x]=1; for (int i=head[x];~i;i=e[i].next) if (e[i].to!=fa && !brg[i]) dfs(e[i].to,x,id); } void dp(int x,int fa) { for (int i=head[x];~i;i=e[i].next) { int y=e[i].to; if (y!=fa) { dp(y,x); maxlen=max(maxlen,f[x]+f[y]+1); f[x]=max(f[x],f[y]+1); } } } ll power(ll x,ll k) { ll ans=1; for (;k;x=x*x%MOD,k>>=1) if (k&1) ans=ans*x%MOD; return ans; } int main() { memset(head,-1,sizeof(head)); scanf("%d%d",&n,&m); for (int i=1,x,y;i<=m;i++) { scanf("%d%d",&x,&y); add(x,y); add(y,x); U[i]=x; V[i]=y; } tot=0; tarjan(1,0); tot=0; for (int i=1;i<=n;i++) if (!vis[i]) dfs(i,0,++tot); memset(head,-1,sizeof(head)); tot=0; for (int i=1;i<=m;i++) if (pos[U[i]]!=pos[V[i]]) add(pos[U[i]],pos[V[i]]),add(pos[V[i]],pos[U[i]]); dp(1,0); cnt-=maxlen; printf("%lld",1LL*cnt*power(m+1,MOD-2)%MOD); return 0; }

__EOF__

本文作者stoorz
本文链接https://www.cnblogs.com/stoorz/p/12109851.html
关于博主:菜死了 /fad
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   stoorz  阅读(559)  评论(1编辑  收藏  举报
编辑推荐:
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
阅读排行:
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 张高兴的大模型开发实战:(一)使用 Selenium 进行网页爬虫
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
点击右上角即可分享
微信分享提示