NOIP2016普及组复赛解题报告

买铅笔

【题目描述】
P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有 3种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起 见,P老师决定只买同一种包装的铅笔。
商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过n支铅笔才够给小朋 友们发礼物。
现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少n支铅笔最少需要花费多少钱。
【输入格式】
输入的第一行包含一个正整数n,表示需要的铅笔数量。
接下来三行,每行用两个正整数描述一种包装的铅笔:其中第一个整数表示这种 包装内铅笔的数量,第二个整数表示这种包装的价格。
保证所有的7个数都是不超过10000的正整数。
【输出格式】
输出一行一个整数,表示P老师最少需要花费的钱。
【样例输入】
57
2 2
50 30
30 27
【样例输出】
54
【分析】
水题,直接模拟即可。

var
  a,b,c,i,n,ans:longint;
begin
  read(n);
    ans:=maxlongint;
    for i:=1 to 3 do begin
      readln(a,b);
        c:=n div a;
        if n mod a<>0 then inc(c);
        if b*cthen ans:=b*c;
    end;
    write(ans);
end.

回文日期

【题目描述】
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。 牛牛习惯用8位数字表示一个日期,其中,前4位代表年份,接下来2位代表月 份,最后2位代表日期。显然:一个日期只有一种表示方法,而两个不同的日期的表 示方法不会相同。
牛牛认为,一个日期是回文的,当且仅当表示这个日期的8位数字是回文的。现 在,牛牛想知道:在他指定的两个日期之间包含这两个日期本身),有多少个真实存 在的日期是回文的。
一个8位数字是回文的,当且仅当对于所有的i ( 1 <=i<= 8 )从左向右数的第i个 数字和第9-i个数字(即从右向左数的第i个数字)是相同的。
例如:
•对于2016年11月19日,用8位数字20161119表示,它不是回文的。
•对于2010年1月2日,用8位数字20100102表示,它是回文的。
•对于2010年10月2日,用8位数字20101002表示,它不是回文的。
每一年中都有12个月份: 其中,1、3、5、7、8、10、12月每个月有31天;4、6、9、11月每个月有30天;而对于2月,闰年时有29天,平年时有28天。
一个年份是闰年当且仅当它满足下列两种情况其中的一种:
1.这个年份是4的整数倍,但不是100的整数倍;
2.这个年份是400的整数倍。
例如:
•以下几个年份都是闰年:2000、2012、2016。
•以下几个年份是平年:1900、2011、2014。
【输入格式】
输入包括两行,每行包括一个8位数字。
第一行表示牛牛指定的起始日期。
第二行表示牛牛指定的终止日期。
保证date_i和都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。
保证date1 —定不晚于date2。
【输出格式】
输出一行,包含一个整数,表示在date1和date2之间,有多少个日期是回文的。
【样例输入】
20000101
20101231
【样例输出】
2
【分析】
回文数的前一半确定后,则整个回文数也就确定了。故枚举年份,直接可以得出与该年份构成回文的月和日,然后直接判断即可。

const
  run:array[1..12]of longint=(31,29,31,30,31,30,31,31,30,31,30,31);
  pin:array[1..12]of longint=(31,28,31,30,31,30,31,31,30,31,30,31);
var
  date1,year1,mon1,day1,date2,year2,mon2,day2,year,mon,day,ans:longint;
    flag:boolean;
function check_run(x:longint):boolean;
begin
  if (x mod 400=0)or((x mod 4=0)and(x mod 100<>0)) then exit(true);
    exit(false);
end;
function make_huiwen(x:longint):longint;
var
  s,s1:string;
    i:longint;
begin
  str(x,s);
    s1:='';
    for i:=length(s) downto 1 do s1:=s1+s[i];
    val(s1,i);
    exit(i);
end;
begin
  read(date1);read(date2);
  year1:=date1 div 10000; mon1:=date1 mod 10000 div 100; day1:=date1 mod 100;
  year2:=date2 div 10000; mon2:=date2 mod 10000 div 100; day2:=date2 mod 100;
  ans:=0;
    for year:=year1 to year2 do begin
      flag:=false;
        mon:=make_huiwen(year) div 100; day:=make_huiwen(year) mod 100;
        if (mon<1)or(mon>12) then continue;
        if day<1 then continue;
        if not check_run(year) then begin
          if day>pin[mon] then continue;
            if year=year1 then
              if (mon>mon1) or ((mon=mon1)and(day>=day1)) then flag:=true;
            if year=year2 then
              if (monor ((mon=mon2)and(day<=day2)) then flag:=true;
            if (year1<year)and(yearthen flag:=true;
        end;
        if check_run(year) then begin
          if day>run[mon] then continue;
            if year=year1 then
              if (mon>mon1) or ((mon=mon1)and(day>=day1)) then flag:=true;
            if year=year2 then
              if (monor ((mon=mon2)and(day<=day2)) then flag:=true;
            if (year1<year)and(yearthen flag:=true;
        end;
        if flag then inc(ans);
    end;
    write(ans);
end.

海港

【题目描述】
小K是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。
小K对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间ti (单位:秒),船上的乘 客数星ki,以及每名乘客的国籍 x(i,1), x(i,2),…,x(i,k);。
小K统计了n艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。
形式化地讲,你需要计算n条信息。对于输出的第i条信息,你需要统计满足 ti - 86400 < tp <= ti的船只p,在所有的x(p,j)中,总共有多少个不同的数。
【输入格式】
第一行输入一个正整数n,表示小K统计了 n艘船的信息。
接下来n行,每行描述一艘船的信息:前两个整数ti和ki分别表示这艘船到达海港的时间和船上的乘客数量,接下来ki个整数x(i,j)表示船上乘客的国籍。
保证输入的ti是递增的,单位是秒;表示从小K第一次上班开始计时,这艘船在第 ti 秒到达海港。
【输出格式】
输出n行,第i行输出一个整数表示第i艘船到达后的统计信息。
【样例输入】
3
1 4 4 1 2 2
2 2 2 3
10 1 3
【样例输出】
3
4
4
【数据范围】
对于70%的数据,1<=n<=1000,所有ki之和<=3000,1<=Xi,j<=1000,1<=ti<=10^9
对于100%的数据,1<=n<=100000,所有ki之和<=300000,1<=Xi,j<=100000,1<=ti<=10^9
【分析】
为了不爆空间,可以边读入边处理,只能开一维数组。

var
  n,i,sum,head,s,tot,j:longint;
    t,k,f:array[1..100000]of longint;
    x:array[1..300000]of longint;
begin
  readln(n);
  read(t[1],k[1]);
  for i:=1 to k[1] do begin
        read(x[s+i]);
        inc(f[x[s+i]]);
        if f[x[s+i]]=1 then inc(sum);
    end;
  writeln(sum);
  s:=s+k[1];head:=1;
  for i:=2 to n do begin
        read(t[i],k[i]);
        for j:=1 to k[i] do begin
            read(x[s+j]);
            inc(f[x[s+j]]);
            if f[x[s+j]]=1 then inc(sum);
        end;
        s:=s+k[i];
        while t[i]-86400>=t[head] do begin
            for j:=tot+1 to tot+k[head] do begin
                dec(f[x[j]]);
                if f[x[j]]=0 then dec(sum);
            end;
            tot:=tot+k[head];
            inc(head);
        end;
        writeln(sum);
    end;
end.

魔法阵

【题目描述】
六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。
大魔法师有m个魔法物品,编号分别为1,2,…,m。每个物品具有一个魔法值,我们用Xi表示编号为i的物品的魔法值。每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。
大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足xa< xb< xc< xd,Xb-Xa=2(Xd-Xc),并且xb-xa<(xc-xb)/3时,这四个魔法物品形成了一个魔法阵,他称这四个魔法物品分别为这个魔法阵的A物品,B物品,C物品,D物品。
现在,大魔法师想要知道,对于每个魔法物品,作为某个魔法阵的A物品出现的次数,作为B物品的次数,作为C物品的次数,和作为D物品的次数。
【输入格式】
第一行包含两个空格隔开的正整数n和m。
接下来m行,每行一个正整数,第i+1行的正整数表示Xi,即编号为i的物品的魔法值。
【输出格式】
共输出m行,每行四个整数。第i行的四个整数依次表示编号为i的物品作 为A,B,C,D物品分别出现的次数。
【样例输入】
30 8
1
24
7
28
5
29
26
24
【样例输出】
4 0 0 0
0 0 1 0
0 2 0 0
0 0 1 1
1 3 0 0
0 0 0 2
0 0 2 2
0 0 1 0
【数据范围】
1<=n<=15000
1<=m<=40000
【分析】
这几乎就是一个数学问题。
令x=d-c(这里的c和d表示题目中的Xc和Xd,为了方便下文均使用下标代替)。
首先可以推出1<=x<=n div 9
然后由Xb-Xa=2(Xd-Xc)可以知道b-a=2x
得出a,代入xb-xa<(xc-xb)/3可得c-b>6x
不难得出b=a+2x,c=a+8x+k(这里的k是一个正整数),d=a+9x+k
于是直接枚举,然后就是统计咯。
注意:下面给的程序中的变量含义可能与分析中的不一样,请结合注释理解。

var
  n,m,i,j,x,y:longint;
    s,f,fa,fb,fc,fd:array[0..40001]of longint;
begin
  read(n,m);
  for i:=1 to m do begin read(s[i]);inc(f[s[i]]); end;
    for i:=1 to n div 9 do begin  //这个变量的含义不想多说了
      x:=9*i+1;y:=0;  //x记录d-a的最小值,即k=1;y记录有多少组符合条件的a和b(a和b看成一个整体)
        for j:=9*i+2 to n do begin  //此处的j枚举的是d,确定c和d的个数
          y:=y+f[j-x]*f[j-x+2*i];
            fd[j]:=fd[j]+y*f[j-i];
            fc[j-i]:=fc[j-i]+y*f[j];
        end;//注意魔法值可能有重复
        x:=8*i+1;y:=0;
        for j:=n-9*i-1 downto 1 do begin  //现在确定a和b的个数
          y:=y+f[j+x]*f[j+x+i];
            fa[j]:=fa[j]+y*f[j+2*i];
            fb[j+2*i]:=fb[j+2*i]+y*f[j];
        end;
    end;
  for i:=1 to m do writeln(fa[s[i]],' ',fb[s[i]],' ',fc[s[i]],' ',fd[s[i]]);
end.

总结:这次考试的AC代码几乎都很短,但是也很难写出来。纵观整套试题,几乎没有考到多少算法,全都是各种编程技巧和思维。所以从这一点来看,NOIP2016比前几年稍难一些。

转载于:https://www.cnblogs.com/JRX2015U43/p/6533483.html

你可能感兴趣的:(NOIP2016普及组复赛解题报告)