因为太迟了,所以没去打。
后面打了Virtual Contest,没想到拿了个rank 3,如果E题更快还能再高,也是没什么想法。
【A】Friends Meeting
题意:
在数轴上有两个整点(x_1=a,x_2=b),要取一个整点(x_3=c)使得(1+2+cdots+|c-a|+1+2+cdots+|c-b|)最小。
题解:
取中点即可。
#include<bits/stdc++.h> using namespace std; int a,b; int main(){ scanf("%d%d",&a,&b); if(a>b) swap(a,b); int md=a+b>>1; printf("%d",(md-a)*(md-a+1)/2+(b-md)*(b-md+1)/2); return 0; }
【B】World Cup
题意:
有(n)支球队比赛,遵循规则:一号和二号比,三号和四号比,五号和六号比,以此类推。
总的过程相当于一场淘汰赛。保证能正好比完。
问两只球队编号为(a,b),如果它们能一起比赛,问是第几场,或者是决赛时输出"Final!"。
题解:
简单模拟,每次球队编号减半。
#include<bits/stdc++.h> int n,a,b; int main(){ scanf("%d%d%d",&n,&a,&b); a+=n-1,b+=n-1; int cnt=0; while(a!=b) a/=2, b/=2, ++cnt; if((1<<cnt)==n) puts("Final!"); else printf("%d",cnt); return 0; }
【C】Laboratory Work
题意:
物理实验课要求测量一个东西的某个数据。另一个人已经测完了,而你还没有。
他的测量比较准确,所以他的数据的最大值和最小值之差不超过2,而且测量值都应该是整数。
你准备抄一抄他测量的数据,但要遵循以下规则:
你的数据和他的数据的平均数要相同,你的数据的最小值不小于他的数据的最小值,你的数据的最大值不超过他的数据的最大值。
满足了以上规则,你还要尽量不让老师发现你是抄的。老师按照如下规则对比你的数据和他的。
老师对他的数据一个个看过去,如果能在你的数据中找到一样的数据,那么把这两个数据都删掉,并且记录相同的数据加一。
最后记录的就是你和他的数据的相似度。
你要让相似度最小,请输出方案。
题解:
挺麻烦的分类讨论,先确定最小最大值,然后如果差1,只能原样输出。
差2就枚举中间值的个数,计算答案。
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=(b);++i) #define ll long long using namespace std; const int INF=0x3f3f3f3f; int n; ll a[100001]; ll sum,MX=-INF,MN=INF,MD; int X1,X2,X3; int Y1,Y2,Y3; int ans=INF,A1,A2,A3; int main(){ scanf("%d",&n); F(i,1,n) scanf("%lld",a+i), sum+=a[i], MX=max(MX,a[i]), MN=min(MN,a[i]); if(MX==MN||MX==MN+1){ printf("%d ",n); F(i,1,n) printf("%d ",a[i]); return 0; } MD=MX-1; F(i,1,n) if(a[i]==MN) ++X1; else if(a[i]==MD) ++X2; else ++X3; Y1=X1, Y2=X2, Y3=X3; int xxxx=min(X1,X3); X1-=xxxx, X3-=xxxx; X2+=xxxx+xxxx; while(X2>=0){ if(ans>min(X1,Y1)+min(Y2,X2)+min(X3,Y3)){ ans=min(X1,Y1)+min(Y2,X2)+min(X3,Y3); A1=X1, A2=X2, A3=X3; } X2-=2; ++X1, ++X3; } printf("%d ",ans); F(i,1,A1) printf("%d ",MN); F(i,1,A2) printf("%d ",MD); F(i,1,A3) printf("%d ",MX); return 0; }
【D】Peculiar apple-tree
题意:
一棵苹果树上有(n)个节点,每个节点都有一个苹果。
每秒,苹果会朝着根的方向滚下来,一秒滚一条边的长度,如果滚到了根,那么就会被采摘掉。
如果两个苹果在同一秒到达了同一个点,它们会湮灭,变成纯粹的能量。
如果5个苹果到达了同一个点,会有两对苹果彼此湮灭,剩下一个苹果继续滚。
问最终能收集到多少苹果。
题解:
水题,树上DFS,按照同一深度的奇偶性贡献答案。
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=(b);++i) #define eF(i,u) for(int i=h[u];i;i=nxt[i]) using namespace std; int n,ans; int cnt[100001]={1},dep[100001]; int h[100001],nxt[100001],to[100001],tot; inline void ins(int x,int y){nxt[++tot]=h[x];to[tot]=y;h[x]=tot;} void DFS(int u){ eF(i,u){ dep[to[i]]=dep[u]+1; ++cnt[dep[u]+1]; DFS(to[i]); } } int main(){ scanf("%d",&n); int x; F(i,2,n) scanf("%d",&x), ins(x,i); DFS(1); F(i,0,n) ans+=(cnt[i]&1); printf("%d",ans); return 0; }
【E】Game with String
题意:
有一个已知的字符串,向左循环移动了(k)位。但是你不知道移动了多少位。
你可以知道这个新的字符串的第一位字符,还可以任意地问一位字符。
假设(k)的值均等地选取,而且你按照最优策略,你一定能猜对的概率是多少?
题解:
仔细看题,仔细分析。
对于同一个首字母的新字符串,放在一起考虑,枚举要问哪一位,然后再放到桶里面去统计个数,正好是1的就可以更新最佳选点。
复杂度(O(n^2))。
#include<bits/stdc++.h> #define F2(i,a,b) for(int i=a;i<(b);++i) #define eF(i,u) for(int i=h[u];i;i=nxt[i]) #define ld double using namespace std; char str[10001]; int n,ans; int cnt[26]; int h[26],siz[26],nxt[10001],to[10001],tot; inline void ins(int x,int y){nxt[++tot]=h[x];to[tot]=y;h[x]=tot;} int main(){ scanf("%s",str); n=strlen(str); F2(i,0,n) ins(str[i]-'a',i), ++siz[str[i]-'a']; F2(i,0,26){ int sums=0; F2(k,0,n){ int sum=0; memset(cnt,0,sizeof cnt); eF(j,i) ++cnt[str[(to[j]+k)%n]-'a']; F2(j,0,26) if(cnt[j]==1) ++sum; sums=max(sums,sum); } ans+=sums; } printf("%f",(ld)ans/n); return 0; }
【F】Teodor is not a liar!
题意:
有一个长度为(m)的线段,现在在这个线段上画上(n)条线,每条线用([l_i,r_i])表示((1le l_ile r_ile m)),表明这条线从(l_i)画到(r_i)。
并且,我们发现这有一个特殊的性质,线段上每个整点都不会被所有画的线覆盖。
现在你要向另一个人解释这件特殊的事情,但是你很懒,你既没有告诉他画了几条线,也不告诉任何一条线的(l_i)或(r_i)。
现在他询问你某些整点位置(x_1,x_2,cdots,x_k)上画的线段的个数,这些整点互不相同。
你想知道(k)最大可以是多大,使得他问完还是不知道这条线段上每个整点是否都不会被所有画的线覆盖。
题解:
题意很奇怪,我们先捋一捋。
问的人不知道画了多少条线,他只能问一些位置的覆盖条数。
记位置(i)的覆盖条数为(a_i)。那么他只能知道(k)个位置的(a_i)。
假设他知道了这些位置的(a_i),什么情况下他无法确定线段上每个整点都不会被所有画的线覆盖?
假设他问了这些位置:(x_1,x_2,cdots,x_k),而且(x_1<x_2<cdots<x_k)。
如果有(a_{x_{i-1}}>a_{x_i})和(a_{x_i}<a_{x_{i+1}}),会有什么情况?
那么我们可以知道所有的覆盖了(x_{i-1})的线段并不都能覆盖到(x_i)上,因为如果覆盖过来了,(a_{x_i})至少要大于等于(a_{x_{i-1}})。
同理也不会覆盖到(x_{i+1}),故有(x_{i+1})没有被所有画的线覆盖。
继续往右推也一样。然后如果用(x_{i+1})往左推也一样。
所以如果出现了(a_{x_{i-1}}>a_{x_i})和(a_{x_i}<a_{x_{i+1}}),就能够确定的确线段上每个整点都不会被所有画的线覆盖。
所以我们要避免出现这个情况,简单的推理得到(a_{x_1},a_{x_2},cdots,a_{x_k})必须是一个单峰序列。
即一个先不降后不升序列。故正反算一遍不下降子序列,枚举中间点更新答案即可。
(a)数组的计算可以使用差分-前缀和技巧。
#include<bits/stdc++.h> #define F(i,a,b) for(int i=a;i<=(b);++i) using namespace std; int n,m,ans; int a[100005],b[100005],f[100005],g[100005]; int main(){ scanf("%d%d",&n,&m); int x,y; F(i,1,n) scanf("%d%d",&x,&y), ++a[x], --a[y+1]; F(i,1,m) a[i]+=a[i-1]; memset(b,0x3f,sizeof b); b[0]=0; F(i,1,m){ f[i]=lower_bound(b,b+m+1,a[i]+1)-b; b[f[i]]=min(b[f[i]],a[i]); } reverse(a+1,a+m+1); memset(b,0x3f,sizeof b); b[0]=0; F(i,1,m){ g[i]=lower_bound(b,b+m+1,a[i]+1)-b; b[g[i]]=min(b[g[i]],a[i]); } F(i,1,m) ans=max(ans,f[i]+g[m-i+1]-1); printf("%d",ans); return 0; }