• 2015苏州大学ACM-ICPC集训队选拔赛(3)题解


    第三次校赛链接:戳我

    1001

     考虑前半组数,我们只需要标记每个数出现的次数,再加上这个数之前的数出现的次数,即为这个数在m次操作中总共需要翻转的次数(即求前缀和),再根据翻转的奇偶性判断最后这个位置上的数有没有翻转即可。后半组数的处理方法类似。

     1 #include<cstdio>
     2 #include<iostream>
     3 #include<cstring>
     4 #include<string>
     5 #include<cmath>
     6 #include<algorithm>
     7 #include<string>
     8 #include<vector>
     9 using namespace std;
    10 int s[1000005];
    11 int m,n,vis[200010]={0},d[100010]={0};
    12 int main()
    13 {
    14 
    15  while(scanf("%d%d",&n,&m)!=EOF)
    16 {
    17     memset(vis,0,sizeof(vis));
    18     for(int i=0;i<n;i++)
    19         scanf("%d",&s[i]);
    20     for(int i=0;i<m;i++)
    21     {
    22         scanf("%d",&d[i]);
    23     }
    24     sort(d,d+m);
    25     for(int i=0;i<m;i++)
    26     {
    27         vis[d[i]-1]++;
    28         vis[n-d[i]]++;
    29     }
    30     for(int i=1;i<n/2;i++)
    31     {
    32         vis[i]+=vis[i-1];
    33         vis[n-i-1]+=vis[n-i];
    34     }
    35     for(int i=0;i<n;i++)
    36     {
    37         if(i>0)
    38             printf(" ");
    39         if(vis[i]%2==0)
    40             printf("%d",s[i]);
    41         else
    42             printf("%d",s[n-i-1]);
    43     }
    44     printf("
    ");
    45 }
    46     return 0;
    47 }
    View Code

    1002

    很明显环中的最大值为a与b的最小公倍数,最小值为最大公因数。需要理论证明的同学请私戳学长。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<cstring>
     5 #define ll long long
     6 using namespace std;
     7 ll Gcd(ll a,ll b)
     8 {
     9     return b?Gcd(b,a%b):a;
    10 }
    11 int main()
    12 {
    13     ll a,b;
    14     while(scanf("%lld %lld",&a,&b)!=EOF)
    15     {
    16         ll gcd=Gcd(a,b);
    17         ll lcm=a/gcd*b;
    18         printf("%lld
    ",lcm-gcd);
    19     }
    20     return 0;
    21 }
    View Code

    1003

    对于一个n*n的棋盘,我们分两种情况讨论:
    第一种情况,n是一个奇数。那么这种时候,棋盘有一个中心点,且总格子数为偶数。
    第二种情况,n是一个偶数。那么这种时候,棋盘没有中心点,且总格子数为偶数。

    当n为偶数时,无论先手如何行动,后手只要保持与其 关于棋盘中心对称 放子,那么就已经是后手的必胜态。具体说明如下:
    马的攻击范围,是与马成3*2或2*3的矩形的 与马对称的顶点。设n*n棋盘上每个格子坐标依次为(1,1)(1,2)…(n,n),倘若先手选择把棋子放置在(i,j)格上,那么后手相对称的放置方法为,放置在(n+1-i,n+1-j)上。现在我们来证明后手总是可 以在(n+1-i,n+1-j)上落子。

    因为格子的坐标都是整数,所以不可能存在先手放置在(i,j)的马能吃到(n+1-i,n+1-j)的情况。又因为开局时棋盘是关于中心对称的,而每一回合后手总是跟随先手对称落子,所以每回合棋盘的各个格子的状态必然是关于棋盘 中心对称的。

    所以易知,当先手能够把棋子放置在(i,j)格上的时候,后手总是可以在(n+1-i,n+1-j)上落子的。即,只要先手还能放,那下一步后手肯定也能放。

    所以,最终先找不到合适位置落子的必然是先手。所以后手 保持与先手关于棋盘中心对称放子 是一种必胜的方法。所以先手无论如何放子,都是先手的必败态。

    当n为奇数时,先手若选择放置在整张棋盘的中心点,而后跟随后手对称落子,则是先手的必胜态。具体说明方法与n为偶数时类似,不再赘述。

     1 #include<iostream>
     2 #include<algorithm>
     3 #include<cstdio>
     4 #include<cstring>
     5 using namespace std;
     6 int main()
     7 {
     8     int n;
     9     while(scanf("%d",&n)!=EOF)
    10     {
    11         if(n%2==0)
    12             cout<<0<<endl;
    13         else
    14             cout<<1<<endl;
    15     }
    16     return 0;
    17 }
    View Code

    1004

    这是一道二进制题,题目上已经有提示了。n个树坑可以看成是n位二进制数,没有种树的位置上值为0,种树的位置上值为1。这样只需要枚举所有的n位二进制数,求出每一位上的值,再判断是否至少有1以及任意两个1之间是否距离至少为m。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5  
     6 using namespace std;
     7 int a[100];
     8  
     9 int main()
    10 {
    11      int n,m,k;
    12      while(~scanf("%d%d%d",&n,&k,&m))
    13      {
    14          int ans=0;
    15          for(int i=0;i<(1<<n);++i)
    16          {
    17              int cnt=0;
    18              memset(a,0,sizeof(a));
    19              int num=i;
    20              while(num!=0)
    21              {
    22                  a[cnt++]=num%2;
    23                  num=num/2;
    24              }
    25              int count=0,pos=1;
    26              num=1;
    27              bool f=true;
    28              for(int j=0;j<cnt;++j)
    29              {
    30                  if(a[j]==1)
    31                  {
    32                     if(num<m&&pos!=1)
    33                          f=false;
    34                     count++;
    35                     num=1;
    36                     pos++;
    37                  }
    38                  else 
    39                      num++;
    40             }
    41              if(f&&count>=k)
    42                  ans++;
    43         }
    44          printf("%d
    ",ans);
    45      }
    46       return 0;
    47 }
    View Code

    1005

    签到题。将所有给出的数转化成10进制数,注意a~f的情况。用一个长度为1000的数组保存每个数出现的次数,最后遍历一遍数组找到出现次数最多的数即可。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5 
     6 using namespace std;
     7 int num[1200];
     8 char a[10]={'a','b','c','d','e','f'};
     9 int main()
    10 {
    11     int t,n;
    12     char s[20];
    13     while(~scanf("%d",&t))
    14     {
    15         memset(num,0,sizeof(num));
    16         while(t--)
    17         {
    18             scanf("%d %s",&n,s);
    19             int c=0;
    20             for(int i=0;i<strlen(s);++i)
    21             {
    22                 if(s[i]>='0'&&s[i]<='9')
    23                 {    
    24                     c=c*n;            
    25                     c+=s[i]-'0';
    26                 }
    27                 else
    28                 {
    29                     c=c*n;
    30                     c+=s[i]-'a'+10;
    31                 }
    32             }
    33             num[c]++;
    34         }
    35         int max=0;
    36         int ans=0;
    37         for(int i=0;i<=1000;++i)
    38         {
    39             if(num[i]>max)
    40             {
    41                 max=num[i];
    42                 ans=i;
    43             }
    44         }
    45         printf("%d
    ",ans);
    46     }
    47      return 0;
    48 }
    View Code

    1006

    很经典的一道题,也算是签到题。解决这道题的方法很多,这里就只给出最优的方案。我们需要保存一个当前最大的子序列和maxsun以及从1开始的子序列的和thissum。当thissum>maxsum时,maxsum赋值为thissum,当thissum<0时,thissum赋值为0。这样遍历一遍数组,thissum每次加上遍历到的数,并进行更新,最后maxsum保存的便是最大的子序列和。

     1 #include<iostream>
     2 #include<cstdio>
     3 #include<cmath>
     4 #include<cstring>
     5 using namespace std;
     6 
     7 int main()
     8 {
     9     long long  ThisSum,MaxSum,i,a,n;
    10        while(~scanf("%d",&n))
    11     {
    12            
    13            ThisSum = MaxSum = 0 ;
    14            for( i=0 ; i<n ; i++ )
    15            {
    16                scanf("%I64d",&a);
    17              ThisSum += a;
    18        
    19                if( ThisSum > MaxSum )
    20                 MaxSum = ThisSum ;
    21                else
    22                 if( ThisSum < 0)
    23                        ThisSum = 0 ;
    24           }
    25         printf("%I64d
    ",MaxSum);
    26     }
    27      return 0;
    28 }
    View Code

    1007

    这道题是鼓励大家勇于提交的,给的两串数其实是没有规律的。而测试样例只有一组,那么唯一的可能就是测试样例在已经给出的数对里面。那么只要判断读入的字符串究竟是其中的哪一个就行了。

     1 #include<stdio.h>
     2 #include<string.h>
     3 char a[][12]={
     4     "0000000001",
     5     "0000000011",
     6     "0000000100",
     7     "0000000101",
     8     "0000000110",
     9     "0000000111",
    10     "0000001000",
    11     "0000001001",
    12     "0000001010",
    13     "0000001011"
    14 };
    15 char b[][12]={
    16     "1000000000",
    17     "1100000000",
    18     "0001000000",
    19     "1001000001",
    20     "0011000100",
    21     "1101000000",
    22     "0001001000",
    23     "1000100010",
    24     "1000100000",
    25     "1001100000"
    26 };
    27 int main(){
    28     char s[12];
    29     scanf("%s",s);
    30     for(int i=0;i<10;i++){
    31         if(strcmp(s,a[i])==0)
    32             puts(b[i]);
    33     }
    34 }
    View Code

    1008

    这道题是对凯撒加密的频数攻击,即依次枚举26个密钥,尝试用这些密钥将文章解密,得到26篇文章,然后比较这26篇文章与统计规律进行比较,找出最符合要求的文章即可。
    重点是如何从26篇文章中筛选出最接近统计规律的文章。通常用的是如下的方法:
    1、设p[i]表示26个字母中第i个字符的统计规律里的频率,我们计算所有26个字母对应的p[i]^2之和,这个值大约是0.065
    2、设q[i]表示用当前密钥k解密后的文章中第i个字符的实际频率,我们计算sigma(p[i]*q[i])得到一个相关值m
    3、对于26个密钥,找出相关值最接近0.065的那个密钥,那么这个就是最后的结果。

     1 #include<cstdio>
     2 #include<cmath>
     3 #include<cstring>
     4 char c[100000000];
     5 double table[27]={0.08167,0.01492,0.02782,0.04253,0.12702,0.0228,0.02015,0.06094,0.06966,0.00153,0.00772,0.04025,0.02406,0.06749,0.07507,0.01929,0.00095,0.05987,0.06327,0.09056,0.02758,0.00978,0.02360,0.00150,0.01974,0.00074};
     6 double f[27];
     7 int cnt[27]={0};
     8 int len,num;
     9 void fun(){
    10     for(int i=0;i<len;i++){
    11         if(c[i]>='a'&&c[i]<='z'){
    12             cnt[c[i]-'a']++;
    13             num++;
    14         }else if(c[i]>='A'&&c[i]<='Z'){
    15             cnt[c[i]-'A']++;
    16             num++;
    17         }
    18     }
    19     for(int i=0;i<26;i++){
    20         f[i]=1.0*cnt[i]/num;
    21     }
    22     int key;
    23     double k=100;
    24     for(int i=0;i<26;i++){
    25         double test=0; 
    26         for(int j=0;j<26;j++){
    27             test+=table[(j-i+26)%26]*f[j];
    28         }
    29         if(fabs(test-0.065)<k){
    30             key=i;
    31             k=fabs(test-0.065);
    32         }
    33     }
    34     for(int i=0;i<len;i++){
    35         if(c[i]>='a'&&c[i]<='z'){
    36             putchar((c[i]-'a'-key+26)%26+'a');
    37         }else if(c[i]>='A'&&c[i]<='Z'){
    38             putchar((c[i]-'A'-key+26)%26+'A');
    39         }else{
    40             putchar(c[i]);
    41         }
    42     }
    43 }
    44 int main(){
    45     //freopen("in.txt","r",stdin);
    46     len=num=0;
    47     char tmp;
    48     while(true){
    49         tmp=getchar();
    50         if(tmp==EOF||tmp=='#'){
    51             fun();
    52             len=num=0;
    53             memset(c,0,sizeof c);
    54             memset(cnt,0,sizeof cnt);
    55             if(tmp==EOF)
    56                 break;
    57             putchar('#');
    58         }else{
    59             c[len++]=tmp;
    60         }
    61     }
    62 }
    View Code

    1009

    基本照着题目的要求写即可,有以下几个注意点:
    1、1也是个无聊的数
    2、输入的x和y的大小不确定,需要先比较一下
    3、对于能表示成a^2+b^2的这个条件,实际上我们可以证明,一个奇素数,如果能够表示成为上述形式,那么当且仅当他模4的余数为1。如果没发现这个定理,其实打表也是可以卡时间过的。

     1 #include<cstdio>
     2 using namespace std;
     3 const int maxi=1000001;
     4 bool vis[maxi];
     5 int sum[maxi];
     6 int main(){
     7     sum[0]=0;
     8     sum[1]=1;
     9     for(int i=2;i<maxi;i++){
    10         sum[i]=sum[i-1];
    11         if(!vis[i]){
    12             if((i%4==1||i==2)&&i%5!=4){
    13                 sum[i]++;
    14             }
    15             for(int j=1;i*j<maxi;j++){
    16                 vis[i*j]=true;
    17             }
    18         }
    19     }
    20     int a,b;
    21     while(scanf("%d%d",&a,&b)==2){
    22         if(a>b){
    23             printf("%d
    ",sum[a]-sum[b-1]);
    24         }else{
    25             printf("%d
    ",sum[b]-sum[a-1]);
    26         }
    27     }
    28 }
    View Code

    1010

    一道模拟题,按照要求写,但是如果对每个样例都循环k次,那么这显然会超时。实际上对于每组输入并不需要循环这么多次,一般循环几次后就会看到循环的终点了,这里有两个终点,153和0,循环到这里就可以停了。

     1 #include<cstdio>
     2 #include<algorithm>
     3 #include<cstring>
     4 #include<vector>
     5 using namespace std;
     6 const int maxi=1000001;
     7 int fun(int n,int k){
     8     int cnt=0;
     9     while(true){
    10         if(n==0)
    11             return 0;
    12         if(n==153)
    13             return 153;
    14         n-=n%8;
    15         n+=n%3==0?0:(3-n%3);
    16         int tmp=0;
    17         while(n>0){
    18             tmp+=(n%10)*(n%10)*(n%10);
    19             n/=10;
    20         }
    21         n=tmp;
    22         cnt++;
    23         if(cnt==k)
    24             return n;
    25     }
    26 }
    27 int main(){
    28     int n,k;
    29     while(scanf("%d%d",&n,&k)==2)
    30         printf("%d
    ",fun(n,k));
    31 }
    View Code
  • 相关阅读:
    Linux文件默认权限和umask笔记
    Linux文件默认权限和umask笔记
    Linux关于文件的权限笔记
    Linux关于文件的权限笔记
    Linux文件和目录权限笔记
    Linux文件和目录权限笔记
    Linux文件目录基础笔记
    Linux文件目录基础笔记
    spark在collect收集数据的时候出现outOfMemoryError:java heap space
    查看hadoop压缩方式
  • 原文地址:https://www.cnblogs.com/yaoyueduzhen/p/5087230.html
Copyright © 2020-2023  润新知