2020.4.18--科大讯飞杯”第18届上海大学程序设计联赛春季赛暨高校网络友谊赛
链接:https://ac.nowcoder.com/acm/contest/5278/A
来源:牛客网
A.组队比赛
时间限制:C/C++ 1秒,其他语言2秒
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
空间限制:C/C++ 524288K,其他语言1048576K
64bit IO Format: %lld
题目描述
Compute, Cubercsl, SuperSodaSea, Ybmj 准备组队参加 "科大讯飞" 赞助的 CrossFire 比赛。
科大讯飞股份有限公司成立于1999年,是亚太地区知名的智能语音和人工智能上市企业。自成立以来,长期从事语音及语言、自然语言理解、机器学习推理及自主学习等核心技术研究并保持了国际前沿技术水平;积极推动人工智能产品研发和行业应用落地,致力让机器"能听会说,能理解会思考",用人工智能建设美好世界。
这场比赛需要两人一队组队参加,他们不知道怎么分组。已知他们的 CrossFire 分数分别为 a, b, c, d,现在想要把他们两两分为一队,使得他们的实力比较平均,也就是两队的实力差尽量小。
这里定义两队的实力差为每队的 CrossFire 分数之和的差值,现在他们想要知道这个实力差最小是多少。
科大讯飞股份有限公司成立于1999年,是亚太地区知名的智能语音和人工智能上市企业。自成立以来,长期从事语音及语言、自然语言理解、机器学习推理及自主学习等核心技术研究并保持了国际前沿技术水平;积极推动人工智能产品研发和行业应用落地,致力让机器"能听会说,能理解会思考",用人工智能建设美好世界。
这场比赛需要两人一队组队参加,他们不知道怎么分组。已知他们的 CrossFire 分数分别为 a, b, c, d,现在想要把他们两两分为一队,使得他们的实力比较平均,也就是两队的实力差尽量小。
这里定义两队的实力差为每队的 CrossFire 分数之和的差值,现在他们想要知道这个实力差最小是多少。
输入描述:
仅一行,包含四个整数 a, b, c, d (1≤a,b,c,d≤36811 \leq a, b, c, d \leq 36811≤a,b,c,d≤3681),中间以空格分隔,分别表示四个人的 CrossFire 分数。
输出描述:
在一行输出一个整数,表示两个队伍实力差的最小值。
示例1
输出
示例2
输入
输出
#include<stdio.h> int main() { int s[5],t,sum; for(int i=0;i<4;i++) { scanf("%d",&s[i]); } for(int i=0;i<4;i++) { for(int j=i+1;j<4;j++) { if(s[i]>=s[j]){ t=s[i]; s[i]=s[j]; s[j]=t; } } } sum=abs(s[0]+s[3]-s[1]-s[2]); printf("%d\n",sum); }
B.每日一报
为了预防新型冠状病毒的侵袭,学校组织学生进行每日一报——自 2020 年 1 月 30 日 起至今,每位同学都必须上报自己的身体状况。为了简化问题,我们规定每日一报仅包含以下信息:
- 报送日期:固定 8 位十进制数,格式为 YYYYMMDD ,如 20200312 代表 2020 年 3 月 12 日;
- 学号:固定 8 位十进制数,不必追溯其具体含义,如 17122490;
- 体温:保留一位小数的浮点数,单位为摄氏度,介于 35.0 至 45.0 之间,如 37.1。
- 报送日期不一致的,则日期较近的在上,日期较久远的在下;
- 报送日期一致体温不一致的,则体温高的在上,体温低的在下;
- 报送日期和体温都一致的,则学号小的在上,学号大的在下。
输入描述:
第一行包含一个整数 n (1≤n≤1001 \leq n \leq 1001≤n≤100),表示需要处理的报送记录。
接下来 n 行,每行代表一条报送记录。
每一条记录包含两个整数和一个浮点数,中间空格分隔,分别表示报送日期、学号和体温,格式如题目描述中所述
输入保证所有的报送日期是 2020 年 1 月 30 日至今(2020 年 4 月 18 日)的合法日期,同一个学号不会在同一天进行多次报送。
输出描述:
第一行输出一个整数 m,表示需要上报备案的记录数量。
接下来 m 行,每行为一条记录。
记录的顺序应符合题目要求,记录格式应与输入格式中所述记录格式保持一致。
备注:
Lemon 很清楚,他其实就是要执行 SQL 查询:SELECT * FROM REPORT WHERE(temperature>=38.0) ORDER BY date DESC, temperature DESC, studentID ASC;
#include<stdio.h> struct student { long long d; long long num; double t; }s[110],b[110],k; int main() { int n; scanf("%d",&n); for(int i=0;i<n;i++) { scanf("%lld %lld %lf",&s[i].d,&s[i].num,&s[i].t); } for(int i=0;i<n;i++) { for(int j=i+1;j<n;j++) { if(s[i].d<s[j].d){ k=s[i]; s[i]=s[j]; s[j]=k; } else if(s[i].d==s[j].d&&s[i].t<s[j].t) { k=s[i]; s[i]=s[j]; s[j]=k; } else if(s[i].d==s[j].d&&s[i].t==s[j].t&&s[i].num>s[j].num){ k=s[i]; s[i]=s[j]; s[j]=k; } } } int sum=0,h=0; for(int i=0;i<n;i++) { if(s[i].t>=38.0){ sum++; b[h++]=s[i]; } } printf("%d\n",sum); for(int i=0;i<sum;i++){ printf("%lld %lld %.1lf\n",b[i].d,b[i].num,b[i].t); } }
3.C-最长非公子序列
Lemon 丢给你两个字母序列 s_1s1 和 s_2s2 ,并无情地交给了你一个奇怪的任务——求最长非公共子序列。
序列 a 是 b 的子序列,当且仅当从 b 中删除一些元素(可以是零个或所有)能得到 a。
例如:
我们可以通过从 "abcde" 中删除 "b" 和 "d" 得到 "ace",因此 "ace" 是 "abcde" 的子序列。
同理 "abcde","e" 和 空串 都是 "abcde" 的子序列;
序列 a 是 b 的子序列,当且仅当从 b 中删除一些元素(可以是零个或所有)能得到 a。
例如:
我们可以通过从 "abcde" 中删除 "b" 和 "d" 得到 "ace",因此 "ace" 是 "abcde" 的子序列。
同理 "abcde","e" 和 空串 都是 "abcde" 的子序列;
但 "abdc" 不是 "abcde" 的子序列。
序列 c 是 s_1s1 和 s_2s2 的非公共子序列当且仅当它满足以下条件中的任何一个:
- c 是 s_1s1 的子序列但不是 s_2s2 的子序列;
- c 是 s_2s2 的子序列但不是 s_1s1 的子序列。
输入描述:
第一行包含一个字符串 s_1s1 ,第二行包含一个字符串 s_2s2。 (1 \leq |s_1|, |s_2| \leq 50001≤∣s1∣,∣s2∣≤5000)
输入保证 s_1s1 和 s_2s2 均只包含小写字母。
输出描述:
在一行输出一个整数,表示最长非公共子序列的长度。
特别地,如果不存在非公共子序列,输出 -1 。
备注:
对于第一个样例:非公共子序列有:"c","ba","bc","aba","abc",其中长度最大为 3。
对于第二个样例:找不到任何非公共子序列。
#include<bits/stdc++.h> using namespace std; int main(){ string s1,s2; while(cin>>s1>>s2){ if(s1.size()!=s2.size()){ cout<<max(s1.size(),s2.size())<<endl;; } else if(s1!=s2){ cout<<s1.size()<<endl; } else{ cout<<"-1\n"; } } return 0; }
4.D-最大字符集
Cubercsl 很喜欢 01 字符串,就像“她喜欢大海”,这一天他想送她一个由 01 字符串组成的集合。
他自然是希望这个集合越多样化越大越好。所以他希望这个集合满足以下条件。
- 每个字符串由 0 和 1 组成。
- 每个字符串长度在 1 到 n 之间,且两两长度不同。
- 集合中任何一个字符串都不是其他字符串的子串。
请你帮他找到满足他要求的最大的集合。
字符串 a 是 b 的子串当且仅当从 b 的头部和尾部删除一些字符(可以是零个或者所有)能得到的 a。
字符串 a 是 b 的子串当且仅当从 b 的头部和尾部删除一些字符(可以是零个或者所有)能得到的 a。
输入描述:
仅一行,包含一个整数 n(1 \leq n \leq 3001≤n≤300)。
输出描述:
第一行输出这个集合的大小 k。
接下来 k 行每行输出一个 01 字符串,表示这个集合的一个元素。
答案不唯一,任何符合要求的答案都会被判为正确。
示例2
输出
复制4 00 110 1010 11111
#include<bits/stdc++.h> using namespace std; int main(){ int n; while(cin>>n){ if(n==1){ cout<<1<<endl<<1<<endl;continue; } if(n==2){ cout<<2<<endl<<0<<endl<<11<<endl;continue; } cout<<n-1<<endl; for(int i=2;i<=n;i++){ for(int j=1;j<=i;j++){ if(j==1||j==i) cout<<"1"; else cout<<"0"; } cout<<endl; } } return 0; }
5.E-美味的序列
Cubercsl 喜欢吃序列,所以这一天不知道谁送了他一个序列,他非常快乐,想把他吃完。
序列的每一段自然也有好吃与不好吃的分别,他用数字标识了每个部分的美味度(可能是负的)。
现在,他可以从头或从尾部花费 1 秒吃掉这个序列的一个元素,并获得这个元素此时的美味度。但是,令他头疼的是,每经过 1秒,所有还没有被吃的部分的美味度会下降 1。
现在 Cubercsl 想知道,如果他按照合理的顺序吃完这个序列,他能获得的最大的美味度的和是多少?
序列的每一段自然也有好吃与不好吃的分别,他用数字标识了每个部分的美味度(可能是负的)。
现在,他可以从头或从尾部花费 1 秒吃掉这个序列的一个元素,并获得这个元素此时的美味度。但是,令他头疼的是,每经过 1秒,所有还没有被吃的部分的美味度会下降 1。
现在 Cubercsl 想知道,如果他按照合理的顺序吃完这个序列,他能获得的最大的美味度的和是多少?
输入描述:
第一行包含一个整数 n (1\leq n \leq 10 ^ 51≤n≤105) ,表示序列的长度。
第二行包含 n 个整数 a_1, a_2 , \ldots ,a_na1,a2,…,an (0 \leq |a_i| \leq 10^90≤∣ai∣≤109),中间以空格分隔,分别表示这个序列每一部分的美味度。
输出描述:
在一行输出一个整数,表示吃完这个序列能获得的最大美味度的和。
示例2
6.F-日期小助手
作为一个关心父母的孩子,Compute 会在每年的母亲节和父亲节为父母准备礼物。可是粗心的他却不记得它们的具体日期了。
已知:母亲节在每年 5 月的第 2 个周日;父亲节在每年 6 月的第 3 个周日。
现在你需要告诉他,下一个(不包括当天)母亲节或父亲节是在什么时候。
已知:母亲节在每年 5 月的第 2 个周日;父亲节在每年 6 月的第 3 个周日。
现在你需要告诉他,下一个(不包括当天)母亲节或父亲节是在什么时候。
输入描述:
第一行包含一个整数 T (T \leq 100T≤100),表示测试数据的组数。
对于每组数据,包含三个整数 y, m, d,中间以空格分隔,分别表示今天的年、月、日。
输入保证是一个在公元 2000 年 1 月 1 日 到 2100 年 12 月 31 日间的合法日期。
输出描述:
对于每组数据,在一行输出下一个需要准备礼物的节日和日期。格式参考样例输出。
示例1
备注:
注意在冒号和逗号后面有一个空格。
#include<bits/stdc++.h> using namespace std; int main(){ int t; cin>>t; while(t--){ int y,m,d; cin>>y>>m>>d; int mo=14-((y-2000)+(y-2000)/4)%7; int fa=21-((y-1998)+(y-2000)/4+1)%7; if(y==2100){ mo=9;fa=20; } //cout<<mo<<"&&"<<fa<<endl; if(m>=1&&m<=4){ printf("Mother's Day: May %dth, %d\n",mo,y);continue; } if(m==5){ if(d<mo) printf("Mother's Day: May %dth, %d\n",mo,y); else{ if(fa!=21) printf("Father's Day: June %dth, %d\n",fa,y); else printf("Father's Day: June %dst, %d\n",fa,y); } continue; } if(m==6){ if(d<fa){ if(fa!=21) printf("Father's Day: June %dth, %d\n",fa,y); else printf("Father's Day: June %dst, %d\n",fa,y); } else{ if(y==2100) mo=8; else if(y==2099) mo=9; else mo=14-((y+1-2000)+(y+1-2000)/4)%7; printf("Mother's Day: May %dth, %d\n",mo,y+1); } continue; } if(m>=7&&m<=12){ if(y==2100) mo=8; else if(y==2099) mo=9; else mo=14-((y+1-2000)+(y+1-2000)/4)%7; printf("Mother's Day: May %dth, %d\n",mo,y+1); } continue; } return 0; }
7.G-血压游戏
Compute 有一棵 n 个点,编号分别为 1\sim n1∼n 的树,其中 s 号点为根。
Compute 在树上养了很多松鼠,在第 i 个点上住了 a_iai 个松鼠。
Compute 在树上养了很多松鼠,在第 i 个点上住了 a_iai 个松鼠。
因为某些缘故,它们开始同时向根节点移动,但它们相当不安分,如果在同一个节点上,它们就会打起来,简单地来说以下事件会依序发生:
- 如果一个节点上有 2 只或 2 只以上的松鼠,他们会打架,然后这个节点上松鼠的数量会减少 1;
- 根节点的所有松鼠移动到地面,位于地面上的松鼠不会再打架;
- 所有松鼠同时朝它们的父节点移动。
所有事件各自都在一瞬间完成,直至树上没有松鼠。
现在 Compute 想知道最终有多少只松鼠到达了地面。
现在 Compute 想知道最终有多少只松鼠到达了地面。
输入描述:
第一行包含两个整数 n, s (1\leq n\leq 2 \cdot 10^51≤n≤2⋅105, 1 \leq s \leq n1≤s≤n),中间以空格分隔,分别表示点的数量和根的编号。
第二行包含 n 个整数 a_1, a_2, \ldots a_na1,a2,…an (0\leq a_i \leq 10^90≤ai≤109),中间以空格分隔,分别表示每个点上一开始的松鼠数量。
接下来 n-1 行,每行包含两个整数 u, v (u \neq vu=v, 1\leq u,v \leq n1≤u,v≤n),中间以空格分隔,表示 u 和 v 之间有一条边。
输入保证是一棵树。
输出描述:
在一行输出一个整数,表示最终到达地面的松鼠数量。
示例2
输出
复制1
#include<bits/stdc++.h> using namespace std; #define M 200005 #define ll long long vector<int>d[M]; int a[M],ln[M],tp[M],son[M],sz[M],fa[M],tot,dp[M],sk[M],fq[M]; struct h{ int p,son1,son2; }q[M]; ll val[M]; void dfs(int x,int f){ dp[x]=dp[f]+1;sz[x]=1;fa[x]=f;ln[x]=++tot; for(int i=0;i<(int)d[x].size();++i){ int v=d[x][i]; if(v==f)continue; dfs(v,x);sz[x]+=sz[v]; if(sz[v]>sz[son[x]])son[x]=v; } } void Dfs(int x,int top){ tp[x]=top; if(son[x])Dfs(son[x],top); for(int i=0;i<(int)d[x].size();++i){ int v=d[x][i]; if(v==son[x]||v==fa[x])continue; Dfs(v,v); } } int Lca(int x,int y){ while(tp[x]!=tp[y]){ if(dp[tp[x]]>dp[tp[y]])x=fa[tp[x]]; else y=fa[tp[y]]; } return dp[x]>dp[y]?y:x; } bool cmp(int a,int b){ if(dp[a]==dp[b])return ln[a]<ln[b]; return dp[a]>dp[b]; } int find(int x){return x==fq[x]?x:fq[x]=find(fq[x]);} bool cmp1(h a,h b){ if(dp[a.p]==dp[b.p])return ln[a.p]<ln[b.p]; return dp[a.p]>dp[b.p]; } int main(){ int n,s;scanf("%d%d",&n,&s); for(int i=1;i<=n;++i)scanf("%d",&a[i]); for(int i=1,x,y;i<n;++i){ scanf("%d%d",&x,&y); d[x].push_back(y); d[y].push_back(x); }dfs(s,0);Dfs(s,s); ll ans=0;int cnt=0; for(int i=1;i<=n;++i)if(a[i])sk[++cnt]=i; sort(sk+1,sk+cnt+1,cmp); for(int st=1;st<=cnt;st++){ int en=st; while(en<=cnt&&dp[sk[en]]==dp[sk[st]])++en; int tot=0;en--; for(int i=st;i<=en;++i)fq[sk[i]]=sk[i],val[sk[i]]=a[sk[i]]; if(st==en){ ans+=max(1,a[sk[st]]-dp[sk[st]]); continue; } for(int i=st;i<en;++i){ int lca=Lca(sk[i],sk[i+1]); q[++tot].p=lca;fq[lca]=lca;val[lca]=0; q[tot].son1=sk[i];q[tot].son2=sk[i+1]; }sort(q+1,q+tot+1,cmp1); for(int i=1;i<=tot;++i){ int x=find(q[i].son1),y=find(q[i].son2); ll w1=0,w2=0; if(x!=q[i].p)w1=max(1ll,val[x]-(dp[x]-dp[q[i].p]));if(y!=q[i].p)w2=max(1ll,val[y]-(dp[y]-dp[q[i].p])); val[q[i].p]+=w1+w2;fq[x]=fq[y]=q[i].p; }ans+=max(1ll,val[q[tot].p]-dp[q[tot].p]); st=en; }printf("%lld",ans); }
8.H-纸牌游戏
Cubercsl 和 Oneday 在玩一个纸牌游戏。两个人手中都有 n 张数字牌,每张牌面上都包含 0 \sim 90∼9 其中一个阿拉伯数字。
游戏规则是需要将手中的牌选出恰好 k 张,组成一个能被 3 整除的非负整数(不能含有多余前导零),组成的数大的获胜。
Cubercsl 自然是想取得胜利,所以他需要找到符合条件的最大的数。
游戏规则是需要将手中的牌选出恰好 k 张,组成一个能被 3 整除的非负整数(不能含有多余前导零),组成的数大的获胜。
Cubercsl 自然是想取得胜利,所以他需要找到符合条件的最大的数。
输入描述:
第一行包含一个整数 T (T \leq 1000T≤1000),表示测试数据的组数。
对于每组测试数据,包含一个数字构成的串 s (1 \leq |s| \leq 10 ^ 51≤∣s∣≤105) 和一个整数 k (1 \leq k \leq |s|1≤k≤∣s∣),中间以空格分隔,分别表示 Cubercsl 手中的牌和要选出的牌的数量。
输入保证 \sum |s| < 10 ^ 6∑∣s∣<106。
输出描述:
对于每组测试数据,在一行输出一个整数,表示最大的能被 3 整除的数。特别地,如果无解,输出 -1。
示例1
示例2
输入
复制5 99999999999999999999 1 99999999999999999999 2 99999999999999999999 3 99999999999999999999 4 99999999999999999999 5
输出
复制9 99 999 9999 99999
#include<cstdio> #include<cctype> #include<queue> #include<cstring> #include<iostream> #include<algorithm> #include<set> #include<cmath> using namespace std; #define reg register typedef long long ll; #define rep(i,a,b) for(reg int i=a,i##end=b;i<=i##end;++i) #define drep(i,a,b) for(reg int i=a,i##end=b;i>=i##end;--i) #define pb push_back template <class T> inline void cmin(T &a,T b){ ((a>b)&&(a=b)); } template <class T> inline void cmax(T &a,T b){ ((a<b)&&(a=b)); } char IO; template<class T=int> T rd(){ T s=0; int f=0; while(!isdigit(IO=getchar())) if(IO=='-') f=1; do s=(s<<1)+(s<<3)+(IO^'0'); while(isdigit(IO=getchar())); return f?-s:s; } const int N=1e5+10; int n,m; char s[N],ans[N]; int cnt[10],c[3]; int Check(int n,int t){ rep(i,0,2) c[i]=0; rep(i,0,9) c[i%3]+=cnt[i]; // x+y=n-i // x<=c[2] ,y<=c[1] // 2x+y=t (mod 3) // x=n-i-y // 2n-2i-2y+y=t(mod 3) // 2n-y-2i=t(mod 3) // y=2n-2i-t (mod 3) int l0=max(0,n-c[1]-c[2]),r0=min(n,c[0]); drep(i,r0,max(l0,r0-4)) { int y=2*n-2*i-t; y=(y%3+3)%3; int l=max(0,n-i-c[2]),r=min(c[1],n-i); // y=的可行范围 while(l%3!=y) l++; if(l<=r) return 1; } return 0; } int main(){ rep(kase,1,rd()) { scanf("%s",s+1),n=strlen(s+1),m=rd(); rep(i,0,9) cnt[i]=0; rep(i,1,n) cnt[s[i]-'0']++; int fl=1,c=0; rep(i,1,m) { ans[i]=0; drep(j,9,0) if(cnt[j]) { cnt[j]--; int t=3-c-j; t=(t%3+3)%3; int fl=Check(m-i,t); if(fl) { ans[i]=j+'0'; c=(c+j)%3; break; } cnt[j]++; } if(!ans[i] || (i==1 && m>1 && ans[i]=='0') ) { fl=0; break; } } ans[m+1]=0; puts(!fl?"-1":ans+1); } }
9.I-古老的打字机
Compute 在他的家里找到了一个打字机,他想要用这个打字机来打字,但是这个打字机实在是太古老了,以至于它根本就无法打出正确的内容。
给定 n 个由小写字母组成的字符串 s_1, s_2, \ldots s_ns1,s2,…sn,每个字符串有一个价值 v_ivi。
现在 Compute 会用这个打字机来打字,他会敲击打字机 m 次,得到字符串 t。其中,每次敲击会等概率地输入一个小写字母或者退格(Backspace)。
退格的作用是把当前得到的字符串的最后一个字符删除。特别地,如果当前字符串是空串,退格后仍为空串。
定义他最后所得到的字符串的价值为:每个串 s_isi 在得到的串 t 中的出现次数 c_ici 与 价值 v_ivi 的乘积。即 \displaystyle\sum_{k = 1} ^ n \sum_{i = 1} ^ {|t|} \sum_{j = i} ^ {|t|} v_k \cdot [s_k = \text{substr}(t, i, j)]k=1∑ni=1∑∣t∣j=i∑∣t∣vk⋅[sk=substr(t,i,j)]
其中 \text{substr}(t, i, j)
给定 n 个由小写字母组成的字符串 s_1, s_2, \ldots s_ns1,s2,…sn,每个字符串有一个价值 v_ivi。
现在 Compute 会用这个打字机来打字,他会敲击打字机 m 次,得到字符串 t。其中,每次敲击会等概率地输入一个小写字母或者退格(Backspace)。
退格的作用是把当前得到的字符串的最后一个字符删除。特别地,如果当前字符串是空串,退格后仍为空串。
定义他最后所得到的字符串的价值为:每个串 s_isi 在得到的串 t 中的出现次数 c_ici 与 价值 v_ivi 的乘积。即 \displaystyle\sum_{k = 1} ^ n \sum_{i = 1} ^ {|t|} \sum_{j = i} ^ {|t|} v_k \cdot [s_k = \text{substr}(t, i, j)]k=1∑ni=1∑∣t∣j=i∑∣t∣vk⋅[sk=substr(t,i,j)]
其中 \text{substr}(t, i, j)
