KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200) 题解

哦淦我已经菜到被ABC吊打了。

A - Century

首先把当前年份减去\(1\),对应的世纪也减去\(1\),然后我们就发现第\(0\)到\(99\)年对应第\(0\)世纪,第\(100\)到\(199\)年对应第\(1\)世纪,以此类推。

答案就是\(\lfloor \frac {N-1} {100} \rfloor\)。这里\(\lfloor x \rfloor\)表示不超过\(x\)的最大整数。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4. ios::sync_with_stdio(false);
  5. cin.tie(0);
  6. cout.tie(0);
  7. int n;
  8. cin>>n;
  9. cout<<(n-1)/100+1<<endl;
  10. return 0;
  11. }

B - 200th ABC-200

乍一看答案会很大,然后爆long long

其实假如当前数不是\(200\)的倍数的时候,把它后面连接上\(200\),结果一定是\(200\)的倍数,因为\(1000\)一定是\(200\)的倍数。

所以答案其实不会很大,最多就是\(976555175781\)(\(N=99999, K=20\))。

然后暴力做就好了。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int main(){
  4. ios::sync_with_stdio(false);
  5. cin.tie(0);
  6. cout.tie(0);
  7. long long n;
  8. int k;
  9. cin>>n>>k;
  10. while(k){
  11. if(n%200==0)n/=200,k--;
  12. else n=n*1000+200,k--;
  13. }
  14. cout<<n;
  15. return 0;
  16. }

C - Ringo's Favorite Numbers 2

假如两个数相减是\(200\)的倍数,他们对\(200\)取模后,得到的结果一定相同。

然后就用前缀和记录在当前的位置之前有多少个数对\(200\)取模得到的结果和这个数对\(200\)取模的结果相同,并且把这个加入答案中就可以了。

只有我一开始以为差一定要是非负数然后被坑了么。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,a[200005],c[205];
  4. long long ans;
  5. int main(){
  6. ios::sync_with_stdio(false);
  7. cin.tie(0);
  8. cout.tie(0);
  9. cin>>n;
  10. for(int i=1;i<=n;i++){
  11. cin>>a[i];
  12. ans+=c[a[i]%200];
  13. c[a[i]%200]++;
  14. }
  15. cout<<ans;
  16. return 0;
  17. }

D - Happy Birthday! 2

首先还是把所有的数都对\(200\)取模。

然后我们可以想出来一种DP的状态表示方法:\(f_{i,j}\)表示已经处理了前\(i\)个数,选出来的\(B\)序列和\(C\)序列的差对\(200\)取模等于\(j\)。

这种状态的转移,可以枚举当前的数是放进\(B\)中还是\(C\)中,亦或是两个序列都不放。输出方案也只需要记录上一个状态的\(j\),然后反着操作一遍就好了。

但是它有一个问题,就是不能保证两个序列都是非空的。

我们可以再开一维,记录两个序列当中是不是有元素了:\(f_{i,j,k}\)表示已经处理了前\(i\)个数,选出来的\(B\)序列和\(C\)序列的差对\(200\)取模等于\(j\),\(k\)以子集的位掩码的形式记录两个序列是否非空。

子集的位掩码这个表述非常有点奇怪,具体来说就是:

  • \(k=0\)表示两个序列都是空的。
  • \(k=1\)表示一个序列都是空的。
  • \(k=2\)表示另一个序列都是空的。
  • \(k=3\)表示两个序列都是非空的。

(“一个”和“另一个”的表述是因为这不重要,我就懒得想了)

然后为了输出方案还要记录上一个状态的\(k\)是多少。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. int n,a[205],g[205][205][4],h[205][205][4];
  4. bool f[205][205][4];
  5. int main(){
  6. ios::sync_with_stdio(false);
  7. cin.tie(0);
  8. cout.tie(0);
  9. cin>>n;
  10. for(int i=1;i<=n;i++){
  11. cin>>a[i];
  12. a[i]%=200;
  13. }
  14. f[0][0][0]=1;
  15. for(int i=1;i<=n;i++){
  16. f[i][0][0]=1;
  17. for(int j=0;j<200;j++){
  18. for(int k=1;k<=3;k++){
  19. if(f[i-1][(j+a[i])%200][k&1]){
  20. f[i][j][k]=1;
  21. g[i][j][k]=-1;
  22. h[i][j][k]=k&1;
  23. }
  24. if(f[i-1][(j+a[i])%200][k]){
  25. f[i][j][k]=1;
  26. g[i][j][k]=-1;
  27. h[i][j][k]=k;
  28. }
  29. if(f[i-1][j][k]){
  30. f[i][j][k]=1;
  31. }
  32. if(f[i-1][(j-a[i]+200)%200][k&2]){
  33. f[i][j][k]=1;
  34. g[i][j][k]=1;
  35. h[i][j][k]=k&2;
  36. }
  37. if(f[i-1][(j-a[i]+200)%200][k]){
  38. f[i][j][k]=1;
  39. g[i][j][k]=1;
  40. h[i][j][k]=k;
  41. }
  42. }
  43. }
  44. }
  45. if(!f[n][0][3]){
  46. cout<<"NO";
  47. return 0;
  48. }
  49. int x=n,y=0,z=3;
  50. cout<<"YES\n";
  51. vector<int> u,v;
  52. while(x){
  53. if(g[x][y][z]==1){
  54. u.emplace_back(x);
  55. z=h[x][y][z];
  56. y=(y-a[x]+200)%200;
  57. }else if(g[x][y][z]==-1){
  58. v.emplace_back(x);
  59. z=h[x][y][z];
  60. y=(y+a[x])%200;
  61. }
  62. x--;
  63. }
  64. reverse(u.begin(),u.end());
  65. cout<<u.size()<<' ';
  66. for(int i:u)cout<<i<<' ';
  67. cout<<'\n';
  68. reverse(v.begin(),v.end());
  69. cout<<v.size()<<' ';
  70. for(int i:v)cout<<i<<' ';
  71. return 0;
  72. }

E - Patisserie ABC 2

这里我们可以想到先枚举三种属性的和是多少。然后我们可以定义\(tot(m)\),表示和为\(m\)的时候,一共有多少种组合方式。

然后由于每个属性的值都是\(1\)到\(n\)的限制,又到了激动人心的分类讨论环节。

首先我们把第一属性的值叫做\(i\),那么后两个属性的和就是\(m-i\)。

  • \(2\leq m-i\leq n+1\):那么第二属性的取值范围就是\(1\)到\(m-i-1\),答案就是\(\sum_{\max(m-n-1,1)}^{\min(n,m-2)}m-i-1\)
  • \(n+2\leq m-i\leq 2n\):那么第二属性的取值范围就是\(m-i-n\)到\(n\),答案就是\(\sum_{\max(1,m-2*n)}^{\min(n,m-n-2)}2n-(m-i)+1\)

那么:

\[tot(i)=\sum_{\max(m-n-1,1)}^{\min(n,m-2)}(m-i-1)+\sum_{\max(1,m-2*n)}^{\min(n,m-n-2)}(2n-(m-i)+1)
\]

然后就可以枚举\(m\),快速的削减\(k\)的值,直到\(k\)小于等于\(tot(m)\)了,这时候我们就枚举第一属性\(i\),然后根据上文的分类讨论计算第二属性和第三属性的取值即可(同样是先快速削减\(k\),然后\(k\)小于等于当前第一属性对应取值种类数时输出答案)。

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. int n;
  5. ll k;
  6. ll sum(int l,int r,int lv,int rv){
  7. return (ll)(lv+rv)*(r-l+1)/2;
  8. }
  9. ll tot(int m){
  10. ll res=0;
  11. if(m<=2*n+1){
  12. int l=max(1,m-n-1),r=min(n,m-2);
  13. res+=sum(l,r,m-l-1,m-r-1);
  14. }
  15. if(m>n+2){
  16. int l=max(1,m-2*n),r=min(n,m-n-2);
  17. res+=sum(l,r,2*n-(m-l)+1,2*n-(m-r)+1);
  18. }
  19. return res;
  20. }
  21. int main(){
  22. ios::sync_with_stdio(false);
  23. cin.tie(0);
  24. cout.tie(0);
  25. cin>>n>>k;
  26. for(int m=3;m<=3*n;m++){
  27. if(k>tot(m)){
  28. k-=tot(m);
  29. continue;
  30. }
  31. for(int i=1;i<=n;i++)if(m-i<=n*2){
  32. if(m-i<=n+1){
  33. if(k>m-i-1){
  34. k-=m-i-1;
  35. continue;
  36. }
  37. cout<<i<<' '<<k<<' '<<m-i-k;
  38. return 0;
  39. }else{
  40. if(k>2*n-(m-i)+1){
  41. k-=2*n-(m-i)+1;
  42. continue;
  43. }
  44. cout<<i<<' '<<m-i-n-1+k<<' '<<n+1-k;
  45. return 0;
  46. }
  47. }
  48. }
  49. return 0;
  50. }

F - Minflip Summation

由于我是看了题解才把这题做出来的,所以就照着题解翻译吧(还好意思讲)。

假如\(S\)不包含?同时\(K=1\)的解法

我们把所有左右相邻的,一个是\(0\)一个是\(1\)的位置(也就是\(S_i\neq S_{i+1}\)的位置\(i\))都数出来(比如10100能数出来\(3\)个)。

为了叙述方便,我们把它叫做“切换位置”(毕竟是从\(0\)切换到\(1\)或者从\(1\)切换到\(0\))。

然后发现一次操作,最多把它减少\(2\)(选择两个切换位置去操作),也可以减少\(1\)(选择一个切换位置,和一个开头/结尾)。

那么假如数出来\(D\)个位置,就需要\(\lfloor \frac D 2 \rfloor\)次操作解决。

这道题的解法

然后对于所有的这样的位置,我们记录他们的总和是\(A\):(主要是从AtCoder的题解上搬的,但是修了一点小锅)

\[A = 2^{Kq} \times ( ( K \times \displaystyle \sum_{i=1}^{|S|-1} f(S_i,S_{i+1}) ) + (K-1) \times f(S_1,S_{|S|}))
\]
\[\begin{eqnarray}
f(a,b)
=
\begin{cases}
\frac{1}{2} & \text{(if at least one of }a\text{ and }b\text{ is ?)} \\
1 & \text{(if neither }a\text{ nor }b\text{ is ? and }a\neq b\text{)} \\
0 & \text{(if neither }a\text{ nor }b\text{ is ? and }a = b\text{)} \\
\end{cases}
\end{eqnarray}
\]

然后答案似乎应该是\(\frac A 2\),不过还有一点问题:假如说一个串数出来奇数个切换位置,这种算法对于这个串会不会少算了半次操作?

我们可以发现,对于有奇数个切换位置的串,多添加一个切换位置后答案相同。

然后假如给\(A\)添加上这些串的个数,答案就是\(\frac A 2\)了。

这些串有一个性质:他们的开头和结尾,是不同的(毕竟切换了奇数次)。

然后这些串的个数就是\(2^{Kq} \times f(S_1,S_{|S|})\)。

然后答案就是\(\frac {A+2^{Kq} \times f(S_1,S_{|S|})} 2\),也就是:

\[2^{Kq} \times K \times \sum_{i=1}^{|S|} f(S_i,S_{i+1})
\]

(特别地,这里把\(S_{|S|+1}\)看作是\(S_1\))

  1. #include<bits/stdc++.h>
  2. using namespace std;
  3. typedef long long ll;
  4. const int mod=1e9+7;
  5. const int inv2=5e8+4;
  6. ll qpow(ll a,ll n){
  7. ll res=1;
  8. while(n){
  9. if(n&1)res=res*a%mod;
  10. a=a*a%mod;
  11. n>>=1;
  12. }
  13. return res;
  14. }
  15. int n,m,k,ans;
  16. char s[100005];
  17. int main(){
  18. ios::sync_with_stdio(false);
  19. cin.tie(0);
  20. cout.tie(0);
  21. cin>>s>>k;
  22. n=strlen(s);
  23. if(n*k==1){cout<<0;return 0;}
  24. for(int i=0;i<n;i++)m+=s[i]=='?';
  25. m=qpow(2,(ll)k*m)*inv2%mod*k%mod;
  26. for(int i=0;i<n;i++){
  27. if(s[i]=='?'||s[(i+1)%n]=='?')ans=(ans+m)%mod;
  28. else if(s[i]!=s[(i+1)%n])ans=(ans+m*2%mod)%mod;
  29. }
  30. cout<<(ll)ans*inv2%mod;
  31. return 0;
  32. }

KYOCERA Programming Contest 2021(AtCoder Beginner Contest 200) 题解的更多相关文章

  1. M-SOLUTIONS Programming Contest 2021(AtCoder Beginner Contest 232) 题解

    目录 G - Modulo Shortest Path H - King's Tour 因为偷懒就只写G和H的题解了. G - Modulo Shortest Path 首先可以观察到对于一条从点\( ...

  2. AtCoder Beginner Contest 076

    A - Rating Goal Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement Takaha ...

  3. AtCoder Beginner Contest 100 2018/06/16

    A - Happy Birthday! Time limit : 2sec / Memory limit : 1000MB Score: 100 points Problem Statement E8 ...

  4. atcoder beginner contest 251(D-E)

    Tasks - Panasonic Programming Contest 2022(AtCoder Beginner Contest 251)\ D - At Most 3 (Contestant ...

  5. AtCoder Beginner Contest 053 ABCD题

    A - ABC/ARC Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement Smeke has ...

  6. AtCoder Beginner Contest 068 ABCD题

    A - ABCxxx Time limit : 2sec / Memory limit : 256MB Score : 100 points Problem Statement This contes ...

  7. AtCoder Beginner Contest 184 题解

    AtCoder Beginner Contest 184 题解 目录 AtCoder Beginner Contest 184 题解 A - Determinant B - Quizzes C - S ...

  8. AtCoder Beginner Contest 255(E-F)

    Aising Programming Contest 2022(AtCoder Beginner Contest 255) - AtCoder E - Lucky Numbers 题意: 给两个数组a ...

  9. AtCoder Beginner Contest 052

    没看到Beginner,然后就做啊做,发现A,B太简单了...然后想想做完算了..没想到C卡了一下,然后还是做出来了.D的话瞎想了一下,然后感觉也没问题.假装all kill.2333 AtCoder ...

随机推荐

  1. Java-ASM框架学习-java概念转字节码概念

    前言 当我们操作字节码的时候,都是和字节码的概念打交道,这让我们很困扰,asm也想到了这点,为了方便,它提供了一个可以把java概念转化为字节码概念的类 import org.objectweb.as ...

  2. 【CSS】水平居中和垂直居中

    水平居中和垂直居中 2019-11-12  15:35:37  by冲冲 1.水平居中 (1)父级元素是行内元素,子级元素是行内元素,子级元素水平居中 ① 设置父级元素为块级元素 display:bl ...

  3. 莫比乌斯反演&各种筛法

    不学莫反,不学狄卷,就不能叫学过数论 事实上大概也不是没学过吧,其实上赛季头一个月我就在学这东西,然鹅当时感觉没学透,连杜教筛复杂度都不会证明,所以现在只好重新来学一遍了(/wq 真·实现了水平的负增 ...

  4. CF#581 (div2)题解

    CF#581 题解 A BowWow and the Timetable 如果不是4幂次方直接看位数除以二向上取整,否则再减一 #include<iostream> #include< ...

  5. windows下的python安装pysam报错

    安装pysam时报错: 指定版本仍报错: 使用pysam-win安装: 但是import时不行: 貌似pysam在windows下难以正常配置,还是在Linux中用吧. https://www.jia ...

  6. Docker实用命令介绍

    Docker实用命令介绍 1. docker启动.关闭.停止 ╭─wil-xz in ~ 12:15:44 ╰─٩(ŏ﹏ŏ.)۶ service docker restart Redirecting ...

  7. Perl 常用的小细节总结

    1.命令行:perl -c perl.pl  #用来检验Perl脚本有没有错误: 2.vi perl.pl打开脚本,ESC+:set nu 回车,给每行加上行号:

  8. vc控制台程序关闭事件时的正确处理方式

    百度可以找到很多关于这个问题解决的方法 关键控制台API函数:SetConsoleCtrlHandler 在支持C++ 11以上的编译器中,你可以这么做. SetConsoleCtrlHandler( ...

  9. 使用Postman轻松实现接口数据关联

    Postman Postman是一款非常流行的HTTP(s)接口测试工具,入门简单,界面美观,功能强大.作为一个测试/开发工程师,这是一款必须要会用的工具.今天以一个实际的案例,来介绍下Postman ...

  10. YARP+AgileConfig 5分钟实现一个支持配置热更新的代理网关

    YARP 是微软开源的一个反向代理项目,英文名叫 Yet Another Reverse Proxy .所谓反向代理最有名的那就是 nginx 了,没错 YARP 也可以用来完成 nginx 的大部分 ...