2021牛客寒假算法基础集训营5

题目链接

2021牛客寒假算法基础集训营5

A.美丽的路径

题目描述

叶妺妺非常喜欢图论题,这天她出了一个图论题,有一个 n 个点 m 条边的无向图,其中第 i 个点的点权为 ai ,她定义一 条点数为 k 路径: b1,b2,bk ;其中点 bi1 与点 bi 通过一条边直接相连 (2ik) ,所以路径中可以出现重复 的点。她将一条路径的美丽值定义为:假设这条路径的点数为 k ,那么这条路径的美丽值就是此路径上的所有点的点 权中第 [k/2+1 小的点权。现在她会询问你是否存在起点为 s 号点并且终点为 t 号点的路径,如果存在则先输出 YES ,再输出存在的所有路径中的最大的美丽值;否则直接输出 NO

输入描述:

有多组样例, 第一行输入一个数 T, 表述样例组数
对于每组样例, 第一行先输入四个数 n,m,s,t, 保证 1sn,1tn
第二行输入 n 个数, 其中第 i 个数为 ai, 表示第 i 个点的点权
接下来输入 m 行, 第 i 行输入两个数 xiyi, 表示点 xi 与点 yi 之间有一条无向边, 保证 1xin,1yi n
【数据规模与约定】

1T10,1n2105,0m2105,1ai109

输出描述:

如果存在起点为 s 号点并且终点为 t 号点的路径, 则第一行输出 YES, 第二行输出存在的所有的路径中的最大的 美丽值; 否则直接输出 NO

示例1

输入

1 5 4 2 4 1 2 3 4 5 1 2 2 3 3 4 4 5

输出

YES 4

解题思路

二分,dfs

二分答案 w,如果 st 的路径上存在相邻的节点点权都大于或等于 w,则由于可以在两点之间无限徘徊使得中位数大于等于当前答案,说明答案可能会更大,另外如果存在一条 st 的路径中大于等于 w 的点的个数不少于少于 w 的点的个数,也说明答案可能会更大,其他情况说明答案大了

  • 时间复杂度:O(nlogn)

代码

// Problem: 美丽的路径 // Contest: NowCoder // URL: https://ac.nowcoder.com/acm/contest/30977/A // Memory Limit: 524288 MB // Time Limit: 6000 ms // // Powered by CP Editor (https://cpeditor.org) // %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=2e5+5; int n,m,s,t,a[N]; bool v[N],vis[N]; vector<int> adj[N]; bool f; bool ck(int x) { for(int i=1;i<=n;i++) if(v[i]&&a[i]>=x) for(int j:adj[i]) if(v[j]&&a[j]>=x)return true; return false; } void ck1(int x,int s1,int s2,int w) { vis[x]=true; if(x==t) { if(s1>=s2)f=true; return ; } for(int y:adj[x]) { if(vis[y])continue; ck1(y,s1+(a[y]>=w),s2+(a[y]<w),w); } } void dfs(int x) { v[x]=true; for(int y:adj[x]) if(!v[y])dfs(y); } int main() { int T; for(cin>>T;T;T--) { cin>>n>>m>>s>>t; int r=0; for(int i=1;i<=n;i++)cin>>a[i],r=max(r,a[i]),adj[i].clear(); while(m--) { int x,y; cin>>x>>y; adj[x].pb(y),adj[y].pb(x); } memset(v,0,sizeof v); dfs(s); if(!v[t]) { puts("NO"); continue; } int l=0; while(l<r) { int mid=l+r+1>>1; f=false; for(int i=1;i<=n;i++)vis[i]=0; ck1(s,a[s]>=mid,a[s]<mid,mid); if(f||ck(mid))l=mid; else r=mid-1; } puts("YES"),cout<<l<<'\n'; } return 0; }

D.石子游戏

题目描述

叶妺妺很喜欢玩石头,于是这天泽鸽鸽给她出了一道石子游戏,规则是这样的:有 n 堆石子排成一行,其中第 i 堆石子 有 ai 个,叶妺妺可以选择做无数次这种操作:每次操作把连续相邻的 k 个石子堆中的每堆石子数目加一,请问叶妺妺 能否让每堆石子的数目都相同呢? 叶妺妺觉得这题太简单了,于是丢给了聪明的你,快来解决这个问题吧!

输入描述:

第一行输入样例组数 T
对于每组样例来说, 第一行输入两个数 nk
第二行输入 n 个数, 其中第 i 个数为 ai
【数据规模与约定】

1T10,1n105,1kn,0ai109

输出描述:

输出总共 T 行, 对于每组样例来说, 如果能使每堆石子的数目都相同则输出一个整数 x,x 表示达到相同时的最 少的操作数; 否则输出 1

示例1

输入

1 4 3 1 1 1 2

输出

1

解题思路

差分

先求出差分数组,最后的目的即第二项后面的数都为 0,而每次差分可将一个位于 i 的数加一,位于 i+k 的数减一,注意操作不用管第 1 项以及 n+1 项,从后往前,可以先把那些正数变为 0,再从前往后把那些负数变为 0,最后检查是否满足要求

  • 时间复杂度:O(n)

代码

// %%%Skyqwq #include <bits/stdc++.h> //#define int long long #define help {cin.tie(NULL); cout.tie(NULL);} #define pb push_back #define fi first #define se second #define mkp make_pair using namespace std; typedef long long LL; typedef pair<int, int> PII; typedef pair<LL, LL> PLL; template <typename T> bool chkMax(T &x, T y) { return (y > x) ? x = y, 1 : 0; } template <typename T> bool chkMin(T &x, T y) { return (y < x) ? x = y, 1 : 0; } template <typename T> void inline read(T &x) { int f = 1; x = 0; char s = getchar(); while (s < '0' || s > '9') { if (s == '-') f = -1; s = getchar(); } while (s <= '9' && s >= '0') x = x * 10 + (s ^ 48), s = getchar(); x *= f; } const int N=1e5+5; LL n,k,t,a[N],d[N]; LL res; int main() { for(cin>>t;t;t--) { cin>>n>>k; for(int i=1;i<=n;i++)cin>>a[i],d[i]=a[i]-a[i-1]; res=0; for(int i=n;i>=2;i--) if(i-k>=1&&d[i]>0)res+=d[i],d[i-k]+=d[i],d[i]=0; for(int i=2;i<=n;i++) if(i+k<=n+1&&d[i]<0)res-=d[i],d[i+k]+=d[i],d[i]=0; bool f=false; for(int i=2;i<=n;i++) if(d[i])f=true; if(f)puts("-1"); else cout<<res<<'\n'; } return 0; }

__EOF__

本文作者acwing_zyy
本文链接https://www.cnblogs.com/zyyun/p/16057689.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角推荐一下。您的鼓励是博主的最大动力!
posted @   zyy2001  阅读(23)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示