Educational Codeforces Round 60 (Rated for Div. 2) 题解
Educational Codeforces Round 60 (Rated for Div. 2)
题目链接:https://codeforces.com/contest/1117
A. Best Subsegment
题意:
给出n个数,选取一段区间[l,r],满足(al+...+ar)/(r-l+1)最大,这里l<=r,并且满足区间长度尽可能大。
题解:
因为l可以等于r,所以我们可以直接考虑最大值,因为题目要求,直接求连续的最大值的长度就是了。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int n; int a[N]; int main(){ cin>>n; int cnt=1,ans=1; for(int i=1;i<=n;i++) scanf("%d",&a[i]); int mx=*max_element(a+1,a+n+1); for(int i=2;i<=n;i++){ if(a[i]==mx && a[i-1]==mx){ cnt++; }else{ ans=max(ans,cnt); cnt=1; } } cout<<max(ans,cnt); return 0; }
B. Emotes
题意:
输入n,m,k,n表示元素个数,每个元素都有其权值;m表示最多可以选取的个数;k表示同一个元素最多被连续选取多少次。
这里每种元素都有无限多个,问怎样选可以使得最终获得权值和最大。
题解:
这个直接贪心就好了。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; ll n,m,k; ll a[N]; int main(){ cin>>n>>m>>k; for(int i=1;i<=n;i++){ scanf("%I64d",&a[i]); } sort(a+1,a+n+1); reverse(a+1,a+n+1); ll fir = a[1],sec = a[2]; ll ans=0; if(fir==sec){ cout<<m*fir; }else{ ans = k*fir+sec; ll tmp = m/(k+1); ans*=tmp; tmp*=(k+1); ans+=(m-tmp)*fir; cout<<ans; } return 0; }
C. Magic Ship
题意:
在二维坐标轴上给出起点和终点的坐标,然后会给n天的天气预报,表示风向,不同的风向对应那一天会多往哪个方向走。天气情况是循环来的 ,循环节为n。
现在问最少要多少天,可以让船从起点走到终点。如果无论如何走不到终点,输出-1。
题解:
这个题我一开始考虑复杂了。其实这里风向带来的位置变化,和船开动的位置变化,可以分开来,也就是说,如果考虑n天的位置变化,可以先看风向给船带来的影响(船会被吹到哪去),再考虑船自身的航行方向。如果想清楚了这一点,那么直接二分天数就好了,最后通过曼哈顿距离来判断可行性。
这里单调性的证明也有点意思,很显然地,时间越短越不可能到终点;另一个方面,假设船在x时间可以到终点,那么时间越长,也更有可能到终点,因为船可以借助风向,在一个位置保持不变。
细节见代码吧(注意二分天数):
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 1e5+5; struct Point{ ll x,y; }st,ed,cur; char s[N]; int n; ll prex[N],prey[N]; int check(ll x){ ll d = x/n; cur.x=st.x+d*prex[n]; cur.y=st.y+d*prey[n]; cur.x=cur.x+prex[x%n]; cur.y=cur.y+prey[x%n]; ll dis=abs(cur.x-ed.x)+abs(cur.y-ed.y); return dis<=x; } int main(){ cin>>st.x>>st.y>>ed.x>>ed.y; cin>>n; scanf("%s",s+1); for(int i=1;i<=n;i++){ prex[i]=prex[i-1];prey[i]=prey[i-1]; prex[i]+=(s[i]=='R'); prex[i]-=(s[i]=='L'); prey[i]+=(s[i]=='U'); prey[i]-=(s[i]=='D'); } ll l=0,r=1e15,mid; while(l<r){ mid=l+r>>1; if(check(mid)) r=mid; else l=mid+1; } if(l==1e15) cout<<-1; else cout<<l; return 0; }
D. Magic Gems
题意:
给出n和m,然后有n个可分解物品,连续的m个可分解物品可以被分解成m个不可分解物品。现在问一共有多少种分解方式,可以让最后都有n个物品(包含可分解与不可分解)。
题解:
这题可以考虑组合数来求解,枚举分解i组物品,那么答案就是C(n-i*(m-1),i),但是这个题行不通,枚举i就爆掉了。
通过考虑第i个数的状态,可以考虑递推:设fi为前i个物品的分解总数,那么fi=fi-1+fi-m,分别对应第i个物品不参与分解以及参与分解。
结合题目数据范围,要用矩阵乘法来加速,具体矩阵构造什么的看代码吧:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 105,MOD = 1e9+7; struct matrix{ ll A[N][N]; int n,m; matrix(){ memset(A,0,sizeof(A)); } void Print(){ for(int i=1;i<=n;i++){ for(int j=1;j<=m;j++){ cout<<A[i][j]<<" "; } cout<<endl; } } }; matrix operator * (const matrix &a,const matrix &b){ matrix ans; ans.n=a.n;ans.m=b.m; for(int i=1;i<=ans.n;i++){ for(int j=1;j<=ans.m;j++){ for(int k=1;k<=b.n;k++){ ans.A[i][j]=(ans.A[i][j]+a.A[i][k]*b.A[k][j]%MOD)%MOD; } } } return ans ; } matrix operator + (const matrix &a,const matrix &b){ matrix ans; ans.n=a.n;ans.m=a.m; for(int i=1;i<=ans.n;i++){ for(int j=1;j<=ans.m;j++){ ans.A[i][j]=(a.A[i][j]+b.A[i][j])%MOD; } } return ans ; } matrix qp_Mat(matrix a,ll b){ matrix ans; ans.n=ans.m=a.n; for(int i=1;i<=ans.n;i++) ans.A[i][i]=1; while(b){ if(b&1) ans=ans*a; a=a*a; b>>=1; } return ans ; } int main(){ ll n,m; cin>>n>>m; matrix trans; trans.n=m;trans.m=m; trans.A[1][1]=trans.A[1][m]=1; for(int i=2;i<=m;i++) trans.A[i][i-1]=1; matrix ans; ans.n=m;ans.m=m; for(int i=1;i<=m;i++) ans.A[i][1]=1; if(n<m){ cout<<1; }else{ matrix Ans = qp_Mat(trans,n-m+1); Ans=Ans*ans; cout<<Ans.A[1][1]; } return 0; }
E. Decypher the String
题意:
每组数据会有n个交换操作,但是这是个交互题不会告诉你。他只会告诉你f(长度为n的串),表示将串进行n次交换操作过后得到的串。
然后你只有三次询问机会,最后输出原串是什么。
题解:
这是一个很有意思的交互题。n最大只有10000,通过观察26^2<n<26^3,那么可以往26这方面考虑一下。
注意到当n<=26时,我们只需要一个所有26个字母的顺序排列,就很容易知道位置的变化情况。
然后构造这样的字符串:aaa...aaa(26*26)bbb....,并且称26*26为一个大段,那么类比上面的情况,通过询问,很容易知道目前第i个位置在原来哪个大段。
然后构造:aaa..aa(26)bbb...bb...zz....,上面每个大段中有26*26个数,也就是我们将每个数的范围限定在了某个长度为26*26的区间中,现在我们构造的字符串,可以进一步将第i个数范围缩小到长度为26的区间。
最后再构造ab..zabc...这样的串,就可以找到第i个数原来的位置在哪里了。
这三个操作的本质其实就是求a*262+b*261+c中,每个位置i对应的a,b,c值,这里的a,b,c都是不超过25的。
是不是感觉十分巧妙...将问题转化为26进制的问题,据说还可以通过crt来搞,但目前我的姿势水平还不够呜呜。
代码如下:
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N = 2e5+5; int n; char str[N],s[N],ans[N]; ll f[N]; int main(){ scanf("%s",str); int len=strlen(str); for(int i=0;i<3;i++){ for(int j=0;j<len;j++){ if(i==0) s[j]='a'+j%26; if(i==1) s[j]='a'+j/26%26; if(i==2) s[j]='a'+j/26/26%26; } printf("? %s\n",s); fflush(stdout); char tmp[N]; scanf("%s",tmp); for(int j=0;j<len;j++){ if(i==0) f[j]+=tmp[j]-'a'; if(i==1) f[j]+=(tmp[j]-'a')*26; if(i==2) f[j]+=(tmp[j]-'a')*26*26; } } char ans[N]; for(int i=0;i<len;i++){ ans[f[i]]=str[i]; } printf("! "); printf("%s",ans); return 0; }
重要的是自信,一旦有了自信,人就会赢得一切。