A题:
A题题目链接
题目描述:
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.
The only line of the input is in one of the following two formats:
Print one integer — the number of candies Limak will save in the year 2016.
4 of week
52
30 of month
11
NotePolar 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题题目链接
题目描述:
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.
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.
Print one integer – the number of years Limak will count in his chosen interval.
5 10
2
2015 2015
1
100 105
0
72057594000000000 72057595000000000
26
NoteIn 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; }上述是本人的解法,可能比较繁琐。
找规律解法
dfs以及数位dp解法
C题:
C题题目链接
题目描述:
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?
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 r1i, c1i, r2i, c2i (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.
Print q integers, i-th should be equal to the number of ways to put a single domino inside the i-th rectangle.
5 8
....#..#
.#......
##.#....
##..#.##
........
4
1 1 2 3
4 1 4 1
1 2 4 5
2 5 5 8
4
0
10
15
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
53
89
120
23
0
2
NoteA 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总是喜欢研究一些奇奇怪怪的东西,有一天,他在数学课上听老师说,任何小数都能表示成分数的形式,于是他信心满满地开始了化了起来,很快他就完成了,但他又想到一个问题,如何把一个循环小数化成分数呢?于是他开始陷入了无尽的思考中......
聪明的acmer,请你帮助我们的QAQ写一个程序,不但可以将普通小数化成最简分数,也可以把循环小数化成最简分数。
3 0.(4) 0.5 0.32(692307)
4/9 1/2 17/52
首先我们考虑将有限小数化成分数,这个比较容易实现。
例如:将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谢谢