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("\n");
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\n",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\n",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\n",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\n",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\n",sum[a]-sum[b-1]);
24         }else{
25             printf("%d\n",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\n",fun(n,k));
31 }
View Code

 

posted @ 2015-12-29 22:09  邀月独斟  阅读(512)  评论(0编辑  收藏  举报