第1周周赛——开学第一课(题解)——出自(Codeforces Good Bye 2015 和 HDU1717)

A题:

A题题目链接

题目描述:

New term and days

TimeLimit:2000MS  MemoryLimit:256MB
64-bit integer IO format: %I64d

Problem Description

Today is Wednesday, the third day of the week. What's more interesting is that tomorrow is the last day of the year 2015.

Limak is a little polar bear. He enjoyed this year a lot. Now, he is so eager to the coming year 2016.

Limak wants to prove how responsible a bear he is. He is going to regularly save candies for the entire year 2016! He considers various saving plans. He can save one candy either on some fixed day of the week or on some fixed day of the month.

Limak chose one particular plan. He isn't sure how many candies he will save in the 2016 with his plan. Please, calculate it and tell him.

Input

The only line of the input is in one of the following two formats:

  • "x of week" where x (1 ≤ x ≤ 7) denotes the day of the week. The 1-st day is Monday and the 7-th one is Sunday.
  • "x of month" where x (1 ≤ x ≤ 31) denotes the day of the month.
Output

Print one integer — the number of candies Limak will save in the year 2016.

SampleInput 1
4 of week
SampleOutput 1
52
SampleInput 2
30 of month
SampleOutput 2
11
    
    
    
    
Note

Polar bears use the Gregorian calendar. It is the most common calendar and you likely use it too. You can read about it on Wikipedia if you want to – https://en.wikipedia.org/wiki/Gregorian_calendar. The week starts with Monday.

In the first sample Limak wants to save one candy on each Thursday (the 4-th day of the week). There are 52 Thursdays in the 2016. Thus, he will save 52 candies in total.

In the second sample Limak wants to save one candy on the 30-th day of each month. There is the 30-th day in exactly 11 months in the 2016 — all months but February. It means that Limak will save 11 candies in total.

题意:

输入一行字符串,字符串可能是以下两种形式:

x of week(1<=x<=7)

x of month(1<=x<=31)

x表示在每一周的该天,或者是每一个月的该天能够存下一颗糖果

问在2016年一整年能够存下多少个糖果(已知2015年12月30日是星期三)

解析:

这道题只要了解了每个月有多少天并且2016年周一到周末分别有多少天即可,注意2016年是闰年(2月有29天)

完整代码实现:

#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int main(){
    int x;
    char str1[10],str2[10];
    while(scanf("%d %s %s",&x,str1,str2)==3){
            if(strcmp(str2,"month")==0){
                if(x<=29){
                    printf("12\n");
                }
                else if(x==30){
                    printf("11\n");
                }
                else if(x==31){
                    printf("7\n");
                }
            }
            else if(strcmp(str2,"week")==0){
                if(x==5||x==6){
                    printf("53\n");
                }
                else{
                    printf("52\n");
                }
            }
    }
    return 0;
}

B题:

B题题目链接

题目描述:

New term and Old Property

TimeLimit:2000MS  MemoryLimit:256MB
64-bit integer IO format: %I64d

Problem Description

The year 2015 is over.

Limak is a little polar bear. He has recently learnt about the binary system. He noticed that the passing year has exactly one zero in its representation in the binary system — 201510 = 111110111112. Note that he doesn't care about the number of zeros in the decimal representation.

Limak chose some interval of years. He is going to count all years from this interval that have exactly one zero in the binary representation. Can you do it faster?

Assume that all positive integers are always written without leading zeros.

Input

The only line of the input contains two integers a and b (1 ≤ a ≤ b ≤ 1018) — the first year and the last year in Limak's interval respectively.

Output

Print one integer – the number of years Limak will count in his chosen interval.

SampleInput 1
5 10
SampleOutput 1
2
SampleInput 2
2015 2015
SampleOutput 2
1
SampleInput 3
100 105
SampleOutput 3
0
SampleInput 4
72057594000000000 72057595000000000
SampleOutput 4
26
    
    
    
    
Note

In the first sample Limak's interval contains numbers 510 = 1012, 610 = 1102, 710 = 1112, 810 = 10002, 910 = 10012 and 1010 = 10102. Two of them (1012 and 1102) have the described property.

题意:

给定两个十进制的整数a,b,计算[a,b]之间用二进制表示只有一个0的数有多少个?(二进制表示时最高位恒为1)

解析:

这道题其实并不难,只要注意处理细节即可。首先我们从题干入手,二进制表示只有一个0的数字有多少个,我们可以先从特殊情况开始,一个个枚举分析。

以n表示二进制的位数,cnt表示二进制表示只有一个0的数字有多少个。

当n = 1时,cnt = 0;

当n = 2时,cnt = 1;(10)

当n = 3时,cnt = 2;(101,110)

当n = 4时,cnt = 3;(1110,1101,1011)

很显然,cnt与n之间的关系为cnt = n-1(除了最高位,每一位都可以为0)这样的话,每个符合要求的数可表示为 2^n-1-2^r(r < n-1)

例如当n = 4的时候,符合要求的数从小到大可表示为:2^4 - 1 - 2^0(1110),2^4 - 1 - 2^1(1101),2^4 - 1 - 2^2(1011)

所以我们先可以找到刚好大于或者等于区间左右端点的2^i的数字,然后记录下这两次i的值,分别表示为cnt1和cnt2,由于cnt2刚好大于或者等于区间的右端点,那么cnt2-1必然是小于区间右端点的,所以呢,在这两个标记值之间[2^cnt1,2^(cnt2-1)]的二进制数字都是题设满足条件的。而剩下的情况则是考虑在[a,2^cnt1) 和 (2^(cnt2-1),b]之间的数字是否满足题设条件即可。

完整代码实现:

#include<cstdio>
#include<algorithm>
#include<cmath>
typedef long long ll;
int main(){
    ll a,b;
    while(scanf("%I64d %I64d",&a,&b)==2&&a&&b){
        if(a==1&&b==2){
           printf("1\n");
            continue;
        }
        ll cnt1,cnt2,ans = 0;
        bool tmp = false;
        for(int i = 0;;i++){
            if((ll)pow(2,i) >= a && !tmp){
                cnt1 = i;
                tmp = true;
            }
            else if((ll)pow(2,i) >= b){
                cnt2 = i;
                break;
            }
        }
        for(int i = cnt1 + 1;i < cnt2;i++){
            ans += i-1;         //中间部分
        }
        ll pre = (ll)pow(2,cnt1),suf = (ll)pow(2,cnt2);
        for(ll j = 0,i = pre - 1 - (ll)pow(2,j);j <=cnt1 - 2;j++,i = pre - 1 - (ll)pow(2,j)){
             if(i>=a && i<=b)       ++ans;           //计算前缀个数
        }
        for(ll j = cnt2 -2,i = suf - 1 - (ll)pow(2,j);j >= 0;j--,i = suf - 1 - (ll)pow(2,j)){
            if(i>=a && i<=b)       ++ans;            //计算后缀个数
        }
        printf("%I64d\n",ans);
    }
    return 0;
}
上述是本人的解法,可能比较繁琐。
另外添加一些B题比较优秀的解法:

找规律解法

dfs以及数位dp解法

C题:

C题题目链接

题目描述:

New term and Domino

TimeLimit:3000MS  MemoryLimit:256MB
64-bit integer IO format: %I64d

Problem Description

They say "years are like dominoes, tumbling one after the other". But would a year fit into a grid? I don't think so.

Limak is a little polar bear who loves to play. He has recently got a rectangular grid with h rows and w columns. Each cell is a square, either empty (denoted by '.') or forbidden (denoted by '#'). Rows are numbered 1 through h from top to bottom. Columns are numbered 1 through w from left to right.

Also, Limak has a single domino. He wants to put it somewhere in a grid. A domino will occupy exactly two adjacent cells, located either in one row or in one column. Both adjacent cells must be empty and must be inside a grid.

Limak needs more fun and thus he is going to consider some queries. In each query he chooses some rectangle and wonders, how many way are there to put a single domino inside of the chosen rectangle?

Input

The first line of the input contains two integers h and w (1 ≤ h, w ≤ 500) – the number of rows and the number of columns, respectively.

The next h lines describe a grid. Each line contains a string of the length w. Each character is either '.' or '#' — denoting an empty or forbidden cell, respectively.

The next line contains a single integer q (1 ≤ q ≤ 100 000) — the number of queries.

Each of the next q lines contains four integers r1ic1ir2ic2i (1 ≤ r1i ≤ r2i ≤ h, 1 ≤ c1i ≤ c2i ≤ w) — the i-th query. Numbersr1i and c1i denote the row and the column (respectively) of the upper left cell of the rectangle. Numbers r2i and c2i denote the row and the column (respectively) of the bottom right cell of the rectangle.

Output

Print q integers, i-th should be equal to the number of ways to put a single domino inside the i-th rectangle.

SampleInput 1
5 8
....#..#
.#......
##.#....
##..#.##
........
4
1 1 2 3
4 1 4 1
1 2 4 5
2 5 5 8
SampleOutput 1
4
0
10
15
SampleInput 2
7 39
.......................................
.###..###..#..###.....###..###..#..###.
...#..#.#..#..#.........#..#.#..#..#...
.###..#.#..#..###.....###..#.#..#..###.
.#....#.#..#....#.....#....#.#..#..#.#.
.###..###..#..###.....###..###..#..###.
.......................................
6
1 1 3 20
2 10 6 30
2 10 7 30
2 2 7 7
1 7 7 7
1 8 7 8
SampleOutput 2
53
89
120
23
0
2
    
    
    
    
Note

A red frame below corresponds to the first query of the first sample. A domino can be placed in 4 possible ways.


题意:

给定一个h*w的矩阵,其中有h*w个元素,‘.’表示为空,而'#'表示该处禁止存放东西,然后每两个连续的.能够放一张卡诺牌,给定一个框定的区域左上角和右下角的坐标,问在这个范围内能够放卡诺牌的张数。

解析:

这道题咋一看暴力就可以直接做,但是需要注意的是,这道题询问次数的范围比较大,达到了10^5次方,因此如果每次都是按行和列依次遍历,那么必然存在大量的重复运算而导致超时。所以我们应该尽量的减少重复运算,而预处理便是一种很好的方式,其实这也就是一道典型的二维预处理题目,预处理在挑战模式——优化中有详细介绍

因此我们可以遍历行和遍历列,记录好每个坐标相对于前一个坐标能放卡诺牌的数量。

然后每次查询的时候,把相应行和相应列的全部加起来即可

完整代码实现:

#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn = 500;
typedef long long ll;
int sum1[maxn+5][maxn+5],sum2[maxn+5][maxn+5];        //用来存储前缀和
char str[maxn+5][maxn+5];
int main(){
    int h,w;
    while(scanf("%d %d",&h,&w)==2&&h&&w){
        for(int i = 1;i <= h;i++)
            for(int j = 1;j <= w;j++){
                sum1[i][j] = 0;         //初始化
                sum2[i][j] = 0;
            }
        for(int i = 1;i <= h;i++){
           getchar();
           for(int j = 1;j <= w;j++){
                scanf("%c",&str[i][j]);
            }
        }
        int i,j;
        for(i = 1;i <= h;i++){         //行遍历
           for(j = 1;j < w;j++){
                if(str[i][j] == '.'&&str[i][j+1] == '.'){
                    sum1[i][j+1] = sum1[i][j] + 1;
                }
                else{
                    sum1[i][j+1] = sum1[i][j];
                }
            }
        }
        for(i = 1;i <= w;i++){         //列遍历
           for(j = 1;j < h;j++){
                if(str[j][i] == '.'&&str[j+1][i] == '.'){
                    sum2[j+1][i] = sum2[j][i] + 1;
                }
                else{
                    sum2[j+1][i] = sum2[j][i];
                }
            }
        }
        int q,r1,c1,r2,c2;
        scanf("%d",&q);
        while(q--){
            ll ans = 0;
            scanf("%d %d %d %d",&r1,&c1,&r2,&c2);
            for(int i = r1;i <= r2;i++){    //行遍历
                    ans += ll(sum1[i][c2] - sum1[i][c1]);
                }
             for(int i = c1;i <= c2;i++){    //列遍历
                    ans += ll(sum2[r2][i] - sum2[r1][i]);
                }
            printf("%I64d\n",ans);

        }
    }
    return 0;
}

F题:

F题题目链接

题目描述:

新问题——QAQ的数学

TimeLimit:1000MS  MemoryLimit:32768KB
64-bit integer IO format: %I64d

Problem Description

总所周知,QAQ总是喜欢研究一些奇奇怪怪的东西,有一天,他在数学课上听老师说,任何小数都能表示成分数的形式,于是他信心满满地开始了化了起来,很快他就完成了,但他又想到一个问题,如何把一个循环小数化成分数呢?于是他开始陷入了无尽的思考中......
聪明的acmer,请你帮助我们的QAQ写一个程序,不但可以将普通小数化成最简分数,也可以把循环小数化成最简分数。

Input
第一行是一个整数N,表示有多少组数据。 
每组数据只有一个纯小数,也就是整数部分为0。小数的位数不超过9位,循环部分用()括起来。 
Output
对每一个对应的小数化成最简分数后输出,占一行。
SampleInput
3
0.(4)
0.5
0.32(692307)
SampleOutput
4/9
1/2
17/52
解析:
这道题一开始我想到是用极限做,例如0.(4),那么其实是等于0.4+0.04+0.004+... 其实这是一个无限项的等比数列,然后用等比数列求和公式处理之后,再将n取极限(n→正无穷),即可解得0.(4) = 4/9.但是这样对于这种比较简单的数据可以处理,但是数据过大或者循环小数的范围比较大的情况下,实现起来比较麻烦。其实这里有一种比较好的方法能够将无限循环小数化成分数的。

首先我们考虑将有限小数化成分数,这个比较容易实现。

例如:将0.35化成分数

0.35 = 35/100,那么我们只需要求得35与100的最大公约数即可,然后两数分别除以该最大公约数,即得分数的最简形式

接下来是无限循环小数,无限循环小数之所以是无限的,是因为这个小数存在着重复出现的部分,那么我们肯定会想,这要是能把无限循环小数化成有限小数不就行了,然后再按照上述操作便可以化出其最简分数形式了。

当然,如果要把无限循环小数化成有限小数的形式,那么我们要去掉他的“尾巴”(即重复循环的部分),这样的话,无限循环小数循环的部分去掉之后,就变成有限小数了。

以样例3分析:

0.32(692307)

该小数循环部分为692307,因此我们要想办法把这一部分去掉,那最简单的方法就是找到其重复的数字,然后两者相减不就化无限为有限了吗?

如果是原来的数字0.32(692307)- 0.32(692307) = 0  这样的话并不能将其化成有限小数的形式,因为这样做并没有任何的作用,那你肯定会说,将其中的一个数乘以10^n不就行了,然后再两者相减,可是具体是乘以多少呢?

我们再分析,能够去掉循环部分,说明两个数具有相同的循环部分,而从第一次开始的循环部分两者相减的话,结果为0,所以我们利用第二次开始的循环部分开始对其进行“去尾“处理。

还是0.32(692307),我们利用第二次开始的循环部分“去尾”的话,那么我设0.32(692307) = X;

则10^6 * X = 326923.07(692307),然后再两者相减,无限循环小数的“尾巴”则去掉了,得

(10^6-1)* X = 326923.07  - 0.32;

然后按照有限小数的处理方法进行处理即可。

所以总结一下将无限循环小数化成分数的方法:

将该数设为X,然后记录其循环位数为cnt位,而后再将X*10^cnt次方,再将两者相减,得

X*(10^cnt - 1) = a(a为常数)

然后解一元一次方程即可。

完整代码实现:

#include<cstdio>
#include<cstring>
#include<cstdlib>
#include<cmath>
using namespace std;
typedef long long ll;
bool have_bracket(char *s);
ll gcd(ll a,ll b);
int main(){
    int N;
    char str[20];
    while(scanf("%d",&N)==1&&N){
        while(N--){
                scanf("%s",str);
                if(have_bracket(str)){
                ll k1 = 0,k2 = 0,i,tmp1 = 0,tmp2 = 0;
                for(i = 2;str[i] != '(';i++){
                    tmp1 = tmp1 * 10 + str[i] - '0';
                    k1++;
                }
                for(int j = i+1;str[j] != ')';j++){
                    tmp2 = tmp2 * 10 + str[j] - '0';
                    k2++;
                }
                ll ans = tmp1 *(ll)pow(10,k2) + tmp2 - tmp1;
                ll ratio_num = (ll)(pow(10,k1+k2) - pow(10,k1));
                ll gcd_num = gcd(ratio_num,ans);
                printf("%I64d/%I64d\n",ans/gcd_num,ratio_num/gcd_num);
            }
            else{
                ll tmp = 0,k = 0;
                for(int i = 2;str[i] != '\0';i++){
                    tmp = tmp * 10 + str[i] - '0';
                    k++;
                }
                ll ans = (ll)pow(10,k);
                ll gcd_num = gcd(ans,tmp);
                printf("%I64d/%I64d\n",tmp/gcd_num,ans/gcd_num);
            }
        }

    }
    return 0;
}
bool have_bracket(char *s){
    for(;*s !='\0';s++){
        if(*s=='('){
            return true;
           }
    }
    return false;
}
ll gcd(ll a,ll b){
    return a % b ? gcd(b,a%b) : b;
}


如有错误,还请指正,O(∩_∩)O谢谢

你可能感兴趣的:(数学,codeforces,无限循环小数化分数,预处理优化,前缀和处理)