2016.3.19纪中模拟赛

前言:

这次考试考的很差,虽然结果还没有出来,但是我的心已经碎了,已经接受被骂的准备了。所以准备写个题解好好反思一下:


题目1:

题目描述
克里特岛居住着一个猎人Hugh Glass。岛上有N(1<=N<=2*10^9)个山洞,依次编号为1,2,...,N。Hugh Glass有一个怪癖,他一生中最讨厌三个数a,b,c(a,b,c互不相等,且1<=a,b,c<=100),他坚决不会进入编号为a、b或者c的倍数的山洞。问你Hugh Glass可以进入多少个山洞?

输入
第一行输入四个整数N,a,b,c。

输出
输出一个整数,表示猎人有多少个山洞可以进入。

数据范围:
50%的数据满足N<=10,000,000;
100%的数据满足1<=N<=2,000,000,000,1<=a,b,c<=100。

样例解释:
只有1号和7号山洞可以进。因为2、4、6、8、10是2个倍数,3、9是3的倍数,5是5的倍数,这8个数不符合要求。


这道题目一看就知道是大大的水题,因为只是求一个容斥问题而已。——but,考试时的我竟然没有做对!一看到题目我就无脑的打程序,不知道a,b如果不互质的话应该是要求他们的最小公倍数,而我直接按(a*b),(b*c),(a*c)去求了——die.

这道题的正解是用n-(n div a)-(n div b)-(n div c)+(n div gbs(a,b))+(n div gbs(a,c))+(n div gbs(b,c))-(n div gbs(gbs(a,b),c)).
gbs是a,b的最小公倍数。可以通过a*b÷最大公因数得来。
代码:
var
        n:qword;
        a,b,c:qword;
function gcd(x,y:qword):qword;
var
        z,a,b:qword;
begin
        a:=x; b:=y;
        while x mod y<>0 do
        begin
                z:=x mod y;
                x:=y;
                y:=z;
        end;
        exit(a*b div y);
end;
begin
        assign(input,'a.in'); reset(input);
        assign(output,'a.out'); rewrite(output);

        readln(n,a,b,c);
        writeln(n-((n div a)+(n div b)+(n div c)-(n div gcd(a,b))-(n div gcd(a,c))-(n div gcd(b,c))+(n div gcd(gcd(a,b),c))));

        close(input); close(output);
end.





题目2:

题目描述:
2016年3月13日,载入史册的一天,李世石执白战胜了阿法狗,虽然1:3败局已定,李世石还是想赢得第5场胜利,为人类扳回一点颜面。
他提出一个新的玩法:N个棋子排成一排,从1到N编号,一开始都是黑色朝上。接下来第1秒,把所有编号为1的倍数的棋子翻转;第2秒,把所有编号为2的倍数的棋子翻转;第3秒,把所有编号为3的倍数的棋子翻转,。。。,这样的操作一直持续了N秒。
现在李世石想考一下阿法狗N秒后一共有多少个棋子是黑色朝上,作为阿法狗软件工程师的你需要设计程序来解决。

输入
第一行输入一个整数N(1<=N<=10^9),表示棋子的数量。

输出
输出N秒后黑色朝上的棋子数量。


样例输入
10

样例输出
7

数据范围:
20%的数据满足:N<=5,000;
40%的数据满足:N<=100,000;
60%的数据满足:N<=1,000,000;
80%的数据满足:N<=10,000,000
100%的数据满足:N<=1,000,000,000。



这道题目很明显就是找规律的题目,n太大了,但我们可以用小数据来模拟一下之后发现每过一个平凡数之后,递增的序列就-1,所以也就是看1~n里有多少个平方数,用n减去就可以了。
代码:
var
        n,i:Longint;
begin
        assign(input,'b.in'); reset(input);
        assign(output,'b.out'); rewrite(output);

        readln(n);
        writeln(n-trunc(sqrt(n)));

        close(input); close(output);
end.




题目三:
题目描述:
结果大家可能都知道了,人机大战第5场依然以人类失败而告终,李世石除了有一颗强大的心脏还有极强的自尊心。
他决定对上面的比赛进行一些改变:N个棋子排成一排,从1到N编号,一开始都是黑色朝上。接下来第1秒,把所有编号为1的倍数的棋子翻转;第2秒,把所有编号为2的倍数的棋子翻转;第3秒,把所有编号为3的倍数的棋子翻转,。。。,这样的操作一直持续了N秒。一直到这里都是跟上题一样。
接着李世石给阿法狗出了T个问题,每个问题都是给定两个整数Li,Ri(1<=Li<=Ri<=N),问编号为Li到Ri的棋子中有多少个棋子被翻转了两次。阿法狗被这样的问题难住了,需要你的帮助。

输入
第一行输入两个整数N(1<=N<=50,000,000),T(1<=T<=100,000),分别表示棋子的数量和问题的数量。接下来T行,每行两个整数Li,Ri,对应着T次询问的区间。

输出
对于每个问题输出一个整数表示对应的区间内被翻转两次的棋子数量。

样例输入
10 3
3 6
2 5
4 9

样例输出
2
3
2
数据范围:
20%的数据满足:N<=1,000,T<=1,000;
40%的数据满足:N<=10,000,T<=10,000;
60%的数据满足:N<=1,000,000,T<=100,000;
80%的数据满足:N<=5,000,000,T<=100,000;
100%的数据满足:N<=50,000,000,T<=100,000。



这道题其实也非常简单,只能被翻两次的棋子必定是一个素数,所以只需找范围内的素数即可,但是这样子你会发现因为t的庞大,会导致你超时,考试的时候我并没有发现这道题的空间给的这么松——512000kb,这是一个什么概念?普通的题目最大都只有这个的一半左右,很难遇到比这个空间还大的了。所以我我觉得50000000个maxLongint是肯定会爆空间的,所以这道题我只拿了40分。

正解是用f[i]表示1~i的素数,然后s~t的序列的素数就等于f[t]-f[s-1]了。
代码:
var
        n,m,i,j,x,y,ans:Longint;
        f:array[0..50000000] of 1..49729071;
        bz:array[1..50000000] of boolean;
begin
        assign(input,'c.in'); reset(input);
        assign(output,'c.out'); rewrite(output);

        readln(n,m);
        bz[1]:=true;
        for i:=2 to trunc(sqrt(n)) do
                if not bz[i] then
                begin
                        j:=2;
                        while i*j<=n do
                        begin
                                bz[i*j]:=true;
                                inc(j);
                        end;
                end;
        for i:=1 to n do
                if not bz[i] then f[i]:=f[i-1]+1 else f[i]:=f[i-1];
        for i:=1 to m do
        begin
                readln(x,y);
                writeln(f[y]-f[x-1]);
        end;


        close(input); close(output);
end.


题目4:
题目描述:
最近好看的节目太多,有CBA总决赛、恒大足球、我是歌手等,但这些离中山都有点远,倒是李克勤和容祖儿的演唱会就在中山体育场,近在咫尺,当然不能错过。于是很多人为了抢到头等票通宵排队。
       0点时有N个人排在售票窗口前,给定N个人距离窗口的距离,有经济头脑的小吃店老板决定选一个位置卖小吃,使得每个排队的人到小吃店的距离之和最短,老板想知道0点时这个最短距离为多少。此外随着时间的推移,排在最后的那个人有些不耐烦了,放弃排队回家睡觉了,每一次发生这样的事情,老板都想调整小吃店的位置,使得所有排队的人到小吃店的距离之和最小。你能帮助他吗?
输入
      第一行输入两个整数N(1<=N<=2,000,000)和M(0<=M<=N),分别表示0点时排队人数,以及放弃排队的人数(注意每次都是最后的那个人放弃)。
      接下来一行N个整数ai(0<=ai<=100,000),描述0点时排队的每个人距离售票窗口的距离,注意这N个数不一定是排好序的。

输出
输出共包括M+1行,分别表示0点时的最短距离以及每次有人放弃排队后的最短距离。

样例输入
5 2
5 1 3 5 2

样例输出
7
5
2

这道题目可以发现,其实集中的位置就是a[(n+1) div 2](a数组是从小到大的),然后我们需要求出所有人到这个集中的位置的和。
如何计算所有人到一个位置的和呢?我们可以先求出一个前缀和,然后再计算。
设s[i]表示A[1~i]的位置总和。
然后我们怎么计算所有人到集中位置的和?
设集中的位置为x.
a数组为:1 2 2 3  5   8   9
s数组为:1 3 5 8 13 21 30
x=3(在第3个人的位置集合)
先计算第3个人右边的,右边=s[n]-s[x]-a[x]*(n-x)。
再计算第3个人左边的,左边=(x-1)*a[x]-s[x-1]。
然后计算完之后,每次就要n-1,因为每次走掉一个人,然后x在更新一下即可。
代码:
var
        n,m,i,j,x,max:Longint;
        s:array[0..2000000] of int64;
        a:array[0..2000000] of Longint;
        f:array[0..100000] of Longint;
procedure doit;
begin
        for i:=0 to max do
                for j:=1 to f[i] do
                begin
                        inc(a[0]);
                        a[a[0]]:=i;
                        s[a[0]]:=s[a[0]-1]+a[a[0]];
                end;
end;

begin
        assign(input,'d.in'); reset(input);
        assign(output,'d.out'); rewrite(output);

        readln(n,m);
        for i:=1 to n do
        begin
                read(x);
                inc(f[x]);
                if x>max then max:=x;
        end;

        doit;
        for i:=1 to m+1 do
        begin
                x:=(n+1) div 2;
                writeln((s[n]-s[x]-a[x]*(n-x))+(a[x]*(x-1)-s[x-1]));
                dec(n);
        end;

        close(input); close(output);
end.


题目5:
题目描述:
     众所周知,纪中是全国最美学校(之一就不加了吧)。其实不仅如此,纪中还是全国十大迷宫之一。一天作为未来纪中人的你来到了迷宫中,迷宫很大,占地800多亩,你陶醉在寿屏公园、逸仙湖、新科学馆、小球馆、书香园、兰溪湖的美景中,出不去了,好不容易找到一个出口,出口处竟然有机关。
     机关处提供4个1到10的数字,允许你用“+”、“-”、“*”、“÷”以及括号把这4个数连接起来,如果得到的结果等于24,就称这4个数字是“吉祥四宝”。如4个数字分别是1、3、5、7,由于(5+1)*(7-1)=24,所以这4个数字是“吉祥四宝”,再比如5、5、5、1,可以通过5*(5-1/5)得到24。机关并不要求你给出这个式子,只要你回答这4个数字是否是“吉祥四宝”即可

输入
第一行输入一个整数T(1<=T<=10),表示问题的数量。
接下来T行,每行输入4个1到10个数字。

输出
   输出T行,每一行对应问题中的4个数字是否是“吉祥四宝”,如果是则输出YES否则输出NO


样例输入
2
1 4 8 9
1 4 2 1

样例输出
YES
NO

这道题目其实很简单,但是考试的时候想的过于复杂了,竟然连题目都没看清(例如:题目其实是没有' '的,但是我竟然莫名其妙的算了' '),还有就是没分析完题目就做,而且思考的时间太久,考试时间多了又不抓紧,这样一道不是很难的题目竟然在考试的时候一分都没拿到。
还是说一下方法吧:Dfs1数字顺序,Dfs2运算符号,for枚举括号,计算js,ans。
计算ans这一部分其实一点也不难,但是需要注意的是如果一个数除以一个不能被整除的数,然后再用一个数减去这个商,再乘以一个数,可能与正确答案有误差
例如3 3 8 8
得到24
可以8/(3-8/3)=24,但是普通算的时候等于23.999999…,所以需要特殊判断一下:
const
        kuo:array[1..7,1..4] of char=(('(',')',' ',' '),
                                      (' ',' ','(',')'),
                                      ('(',')','(',')'),
                                      (' ','(',')',' '),
                                      (' ',' ',' ',' '),
                                      ('(',' ',')',' '),
                                      (' ','(',' ',')'));
var
        a:array[1..4] of Longint;
        ch,temp:array[1..3] of char;
        bz:array[1..4] of boolean;
        b,c:array[1..4] of real;
        flag:boolean;
        n,i,k:Longint;
        sum:real;
procedure ok(x,y:Longint);
begin
        if ch[x]='+' then c[y]:=c[x]+c[y];
        if ch[x]='-' then c[y]:=c[x]-c[y];
        if ch[x]='*' then c[y]:=c[x]*c[y];
        if (ch[x]='/') and (c[y]<>0) then c[y]:=c[x]/c[y];
        c[x]:=0;
        ch[x]:=#0;
end;
procedure ko(x,y:Longint);
var
        i,j:Longint;
begin
        for i:=x to y-1 do
                if ch[i]='*' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        c[j]:=c[i]*c[j];
                        c[i]:=0;
                        ch[i]:=#0;
                end
                else
                if ch[i]='/' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        if c[j]<>0 then
                        begin
                                c[j]:=c[i]/c[j];
                                c[i]:=0;
                                ch[i]:=#0;
                        end;
                end;
        for i:=x to y-1 do
                if ch[i]='+' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        c[j]:=c[i]+c[j];
                        c[i]:=0;
                        ch[i]:=#0;
                end
                else if ch[i]='-' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        c[j]:=c[i]-c[j];
                        c[i]:=0;
                        ch[i]:=#0;
                end;
end;
function jisuan:real;
var
        i,j:longint;
begin
        temp:=ch;
        c[1]:=b[a[1]]; c[2]:=b[a[2]]; c[3]:=b[a[3]]; c[4]:=b[a[4]];
        case k of
                1:ok(1,2);
                2:ok(3,4);
                3:begin
                        ok(1,2);
                        ok(3,4);
                end;
                4:ok(2,3);
                6:ko(1,3);
                7:ko(2,4);
        end;

        for i:=1 to 3 do
                if ch[i]='*' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        c[j]:=c[i]*c[j];
                        c[i]:=0;
                        ch[i]:=#0;
                end
                else if ch[i]='/' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        if c[j]<>0 then
                        begin
                                c[j]:=c[i]/c[j];
                                c[i]:=0;
                                ch[i]:=#0;
                        end;
                end;
        for i:=1 to 3 do
                if ch[i]='+' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        c[j]:=c[i]+c[j];
                        c[i]:=0;
                        ch[i]:=#0;
                end
                else if ch[i]='-' then
                begin
                        for j:=i+1 to 4 do
                                if c[j]>0 then break;
                        c[j]:=c[i]-c[j];
                        c[i]:=0;
                        ch[i]:=#0;
                end;
        ch:=temp;
        exit(c[4]);
end;
procedure kuohao;
begin
        for k:=1 to 7 do
                if (jisuan=24) or (24>jisuan) and (24-jisuan<=0.0000000001) then
                begin
                        flag:=true;
                        exit;
                end;
end;
procedure dg(k:LOngint);
var
        i:Longint;
begin
        if k>3 then
        begin
                kuohao;
                exit;
        end;
        for i:=1 to 4 do
        begin
                case i of
                        1:ch[k]:='+';
                        2:ch[k]:='-';
                        3:ch[k]:='*';
                        4:ch[k]:='/';
                end;
                dg(k+1);
                ch[k]:=#0;
        end;
end;
procedure dfs(k:Longint);
var
        i:Longint;
begin
        if k>4 then
        begin
                dg(1);
                exit;
        end;
        for i:=1 to 4 do
                if bz[i] then
                begin
                        bz[i]:=false;
                        a[k]:=i;
                        dfs(k+1);
                        a[k]:=0;
                        bz[i]:=true;
                end;
end;

begin
        assign(input,'e.in'); reset(input);
        assign(output,'e.out'); rewrite(output);

        readln(n);
        for i:=1 to n do
        begin
                readln(b[1],b[2],b[3],b[4]);
                fillchar(bz,sizeof(bz),true);
                flag:=false;
                dfs(1);
                if flag then writeln('YES') else writeln('NO');
        end;

        close(input); close(output);
end.


总结:

这次考试考得不好,但是不在意成绩,而在于所得的收获,经过这次比赛,我收获颇深,总结出以下需要改正的几点:

总结1:

在编程序的时候有时不止编一个程序,像老师说有时复杂的题目要编7,8个程序,当然以我们的水平现阶段也不太可能遇到这种题目。但是,有一点是关键的,就是编这么多个程序拿来干嘛?拿来对拍。对拍是一定要做的,不能因为题目简单就不对拍。像这次比赛的第一道题目,我一拿起来就做,想也不想清楚就做完之后就看第二题了。而是应该做完之后看一下写的两个程序的答案是否一致(以暴力算法的答案为标准)。

总结2:

想题目时一定要想清楚再编程。像这次比赛的第三题,只拿了40分,为什么?就是因为想的不够严谨。512000kb=512000千字节(kb)=500兆字节(mb)。而longint仅是4字节,5000000*4=20000000字节1字节(b),千字节(kb),兆字节(mb),千兆字节(kb),而他们之间的进率都是1024,所以——500mb=500*1024*1024=524288000b>20000000b,所以这次收获到了一个计算空间的方法,下次就可以这样子计算了。

总结3:

要把问题的精华留下,所有修饰去掉,留最本质的东西即可,像第一题,就可以简化为(一个1~n序列中不能被a,b,c整除的数的个数)即可。第二题,求1~n有多少个平方数即可。第三题,x~y的一个序列当中有多少个素数即可。第四题,如何确定位置使得所有人走的路径最小,每走一个人的最小路径又是多少即可。第五题,求如何用4个数字,4种运算符号,加上括号得到24即可。

总结4:
做题目时要争夺每一分,哪怕只是一分,也要得到,尽量的多得分,才是王道。
总结5:
做题目时不仅要多分,还要抓紧每一分每一秒去想题,千万不能想别的,这才能发挥出最好水平,如果因为前面的题目做的快了,就把精神放轻松的话,就有可能像这次连很简单的第5题都做不出来了。

所以这5个总结就是这次比赛的最大收获了,明天的市赛一定要好好发挥!
送自己几句话:最佳状态,心无杂念,勿粗心大意,需仔仔细细,做完对拍,抓紧每秒,易题必对,难题必争,若做到这些,失败也无谓。
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值