2018-8-27【训练日记】

  时间略紧,还以为是周二,,还想着将题目整理完好好写写,现在现把自己找到的资料哦总结一下吧,,我们A了三个题,有7个题没做出来,其中有三道题目是有感觉可以加把劲有希望的,,

其中一道模拟题,我有了大体的思路,但当时大家既听不进去,又寄希望于其他的题目,细节方面都没有实现,找到一篇博文,写的还挺详细的【原题啊,,】

https://blog.csdn.net/Wang_1997/article/details/65451808

大体说一下:

问题描述

        你的一个朋友买了一台电脑。他以前只用过计算器,因为电脑的显示器上显示的数字的样子和计算器是不一样,所以当他使用电脑的时候会比较郁闷。为了帮助他,你决定写一个程序把在电脑上的数字显示得像计算器上一样。

输入数据

       输入包括若干行,每行表示一个要显示的数。每行有两个整数 s 和 n (1 <= s <= 10, 0 <= n <= 99999999),这里 n 是要显示的数, s 是要显示的数的尺寸。
       如果某行输入包括两个 0,表示输入结束。这行不需要处理。

输出要求

       显示的方式是:用 s 个'-'表示一个水平线段,用 s 个'|'表示一个垂直线段。这种情况下,每一个数字需要占用 s+2 列和 2s+3 行。另外,在两个数字之间要输出一个空白的列。在输出完每一个数之后,输出一个空白的行。注意:输出中空白的地方都要用空格来填充。

输入样例

2 12345

3 67890

0 0

输出样例

     --  --      --
   |   |   ||  ||
   |   |   ||  ||
     --  --  --  --
   ||      |   |   |
   ||      |   |   |
     --  --      --
 ---  ---  ---  ---  ---
|        ||   ||   ||   |
|        ||   ||   ||   |
|        ||   ||   ||   |
 ---       ---  ---
|   |    ||   |    ||   |
|   |    ||   |    ||   |
|   |    ||   |    ||   |
 ---       ---  ---  ---

解题思路

        一个计算器上的数字显示单元,可以看作由以下编号从 1 到 7 的 7 个笔画组成:

 

    我们可以说,数字 8 覆盖了所有的笔画,数字 7 覆盖笔画 1、 3 和 6,而数字 1覆盖笔画 3、 6。注意,每个笔画都是由 s 个’-‘或 s 个’|’组成。
        输出时,先输出第 1 行,即整数 n 中所有数字里的笔画 1,然后输出第 2 行到第 s+1 行,即所有数字的笔画 2 和笔画 3,接下来是第 s+2 行,即所有数字的笔画 4,再接下来是第 s+3行到 2×s+2 行,,就是所有数字的笔画 5 和笔画 6,最后的第 2×s+3 行,是所有数字的笔
画 7。如果某个数字 d 没有覆盖某个笔画 m (m = 1…7),那么,输出数字 d 的笔画 m 的时候,就应该都输出空格;如果覆盖了笔画 m,则输出 s 个’-‘或 s 个’|’,这取决于笔画 m 是横的还是竖的。
        由上思路,解决这道题目的关键,就在于如何记录每个数字都覆盖了哪些笔画。实际上,如果我们记录的是每个笔画都被哪些数字覆盖,则程序实现起来更为容易。一个笔画被哪些数字所覆盖,可以用一个数组来记录,比如记录笔画 1 覆盖情况的数组如下:
        char n1[11] = {"- -- -----"};
        其中, n1[i](i = 0……9) 代表笔画 1 是否被数字 i 覆盖。如果是,则 n1[i] 为'-',如果否,则 n1[i]为空格。上面的数组的值体现了笔画 1 被数字 0, 2, 3, 5, 6, 7, 8, 9 覆盖。对于竖向的笔画 2,由字符 '|' 组成,则记录其覆盖情况的数组如下:
        char n2[11] = {"|   ||| ||"};
        该数组的值体现了笔画 2 被数字 0, 4, 5, 6, 8, 9 覆盖。

关键代码:

char n1[11] = {"- -- -----"}; //笔画 1 被数字 0, 2, 3, 5, 6, 7, 8, 9 覆盖
char n2[11] = {"|   ||| ||"}; //笔画 2 被数字 0, 4, 5, 6, 8, 9 覆盖
char n3[11] = {"|||||  |||"}; //笔画 3 被数字 0, 1, 2, 3, 4, 7, 8, 9 覆盖
char n4[11] = {"  ----- --"}; //笔画 4 被数字 2, 3, 4, 5, 6, 8, 9 覆盖
char n5[11] = {"| |   | | "}; //笔画 5 被数字 0, 2, 6, 8 覆盖
char n6[11] = {"|| |||||||"}; //笔画 6 被数字 0, 1, 3, 4, 5, 6, 7, 8, 9 覆盖
char n7[11] = {"- -- -- --"}; //笔画 7 被数字 0, 2, 3, 5, 6, 8, 9 覆盖
int main()
{
	int s;
	char szNumber[20];
	int nDigit,nLength,i,j,k;
	while(true){
		cin >> s >> szNumber;
		nLength = strlen(szNumber);
		if(s == 0){ 
			break;
		}		
		for(i=0;i<nLength;i++){//输出所有数字的笔画 1
			nDigit = szNumber[i] - '0';
			cout<<" ";
			for(j=0;j<s;j++){ //一个笔画由 s 个字符组成
				cout<<n1[nDigit];
			}
			cout<<" ";
		}
		cout<<endl;
		for(i=0;i<s;i++){//输出所有数字的笔画 2 和笔画 3
			for(j=0;j<nLength;j++){
				nDigit = szNumber[j]-'0';
				cout<<n2[nDigit];
				for(k=0;k<s;k++){
					cout<<" ";//笔画 2 和笔画 3 之间的空格
				}
				cout<<n3[nDigit];
			}
			cout<<endl;
		}
		for(i=0;i<nLength;i++){//输出所有数字的笔画 4
			nDigit = szNumber[i] - '0';
			cout<<" ";
			for(j=0;j<s;j++){
				cout<<n4[nDigit];
			}
			cout<<" ";
		}
		cout<<endl;
		for(i=0;i<s;i++){//输出所有数字的笔画 5 和笔画 6
			for(j=0;j<nLength;j++){
				nDigit = szNumber[j]-'0';
				cout<<n5[nDigit];
				for(k=0;k<s;k++){
					cout<<" ";//笔画 5 和笔画 6 之间的空格
				}
				cout<<n6[nDigit];
			}
			cout<<endl;
		}
		
		for(i=0;i<nLength;i++){ //输出所有数字的笔画 7
			nDigit = szNumber[i] - '0';
			cout<<" ";
			for(j=0;j<s;j++){
				cout<<n7[nDigit];
			}
			cout<<" ";
		}
		cout<<endl;
	} 
	return 0;
}

这个思路可以膜拜一下了,写出来代码简洁,我试图将每一个数字用二维数组表示出来,只是表示下来,就用了大半时间,又不懂到底怎么进行内部循环,最终也没有解出来,感觉还是有技巧的,再给我点时间,也不一定能够有这么简洁的思路。


以及一个杭电原题:

Problem B. Harvest of Apples

及其题解:https://blog.csdn.net/codeswarrior/article/details/81359075

其中涉及的莫队算法讲解:https://www.cnblogs.com/CsOH/p/5904430.html

我们将其理解为了卢卡斯定理的打表运用,当然是超时,,

D: 刘小强的苹果树

题目描述

刘小强的河里有n块石头,从1到n,请你帮他计算最多选取m块石头的方法数。

输入

第一行测试样例数T.

每个样例一行,两个数  n,m,(T<=1e5,n<=1e5,m<=1e5)

输出

输出方法数模1e9+7.

样例输入

2
5 2
1000 500

样例输出

16
924129523

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn = 1e5+5;
const int mod = 1e9+7;
ll fac[maxn],inv[maxn];
ll rev2;
struct Query{
    int L,R,id,block;
    bool operator < (const Query &p)const{//按照分块排序,再按照右端点排序
        if(block == p.block) return R < p.R;
        return block < p.block;
    }
}Q[maxn];
ll res;
ll ans[maxn];

ll q_pow(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b & 1)
            ans = ans * a % mod;
        b >>= 1;
        a = a * a % mod;
    }
    return ans;
}//快速幂取模

ll C(int n,int k){
    return fac[n] * inv[k] % mod * inv[n-k] % mod;
}//组合数公式

void init(){
    rev2 = q_pow(2,mod-2);
    fac[0] = fac[1] = 1;
    for(int i = 2; i < maxn; i++){
        fac[i] = i * fac[i-1] % mod;
    }//预处理阶乘
    inv[maxn-1] = q_pow(fac[maxn-1],mod-2);
    for(int i = maxn-2; i >= 0; i--){
        inv[i] = inv[i+1] * (i + 1) % mod;
    }//预处理阶乘的逆元(很巧妙,不需要每次求逆元了)
}

inline void addN(int posL,int posR){//因为传进来的posL已经加1了,所以求S(posL,posR)=2S(posL-1,posR)-C(posL-1,posR)
                                    //而S(posL-1,posR)就是上一次的结果res,故只需要算C(posL-1,posR)
    res = (2 * res % mod - C(posL-1,posR) + mod) % mod;
}

inline void addM(int posL,int posR){//因为传进来的posR已经自增完成,res是上一次的结果S(posL,posR-1)故只需要求C(posL,posR)
    res = (res + C(posL,posR)) % mod;
}

inline void delN(int posL,int posR){//因为传进来的是后缀自增,所以posL还是原来的值
                                    //那么新的S(posL-1,posR)=(S(posL,posR)+C(posL-1,posR))/2,其中S(posL,posR)就是res
    res = (res + C(posL-1,posR)) % mod * rev2 % mod;
}

inline void delM(int posL,int posR){//因为传进来的是后缀自增,所以posR还是原来的值
                                    //那么新的S(posL,posR-1)=S(posL,posR)-C(posL,posR),其中S(posL,posR)就是res
    res = (res - C(posL,posR) + mod) % mod;
}

int main(){
    int T;
    init();
    int len = (int)sqrt(maxn*1.0);
    scanf("%d",&T);
    for(int i = 1; i <= T; i++){
        scanf("%d%d",&Q[i].L,&Q[i].R);
        Q[i].id = i;//记录下查询顺序编号
        Q[i].block = Q[i].L / len;//块号
    }
    sort(Q+1,Q+1+T);//排序
    res = 2;
    int curL = 1,curR = 1;
    for(int i = 1; i <= T; i++){
        while(curL < Q[i].L) addN(++curL,curR);//需要算S(curL+1,curR)=2S(curL,curR)-C(curL,curR)
        while(curR < Q[i].R) addM(curL,++curR);//需要算S(curL,curR+1)=S(curL,curR)+C(curL,curR+1)
        while(curL > Q[i].L) delN(curL--,curR);//需要算S(curL-1,curR)=(S(curL,curR)+C(curL-1,curT))/2
        while(curR > Q[i].R) delM(curL,curR--);//需要算S(curL,curR-1)=S(curL,curR)-C(curL,curR)
        ans[Q[i].id] = res;
    }
    for(int i = 1; i <= T; i++){
        printf("%lld\n",ans[i]);
    }
    return 0;
}

要命的是什么,有一支队伍由于失误,没能A掉这个题,他们明白算法且能够写出来,最后讨论的时候,他们疑惑:你们知道思路了,写不出来代码吗?当时很震惊,那么长的模板,,用一个数学公式写出来吗??,后来想想,我们不是会不会写模板的问题,是理不理解模板的运营过程以及这个模板是用来做什么的,我们都不清楚


以及一道非常遗憾的题目,取款机,很多人都A了,我们由于模板整理的不到位,思路什么的都是对的,只是最后一直没A出来,还是很可惜的。

莫名其妙就感冒了,,在从头学小蓝书,好好刷题,好好撸书,,

猜你喜欢

转载自blog.csdn.net/sodacoco/article/details/82861343
今日推荐