2016普及组(复赛)
尝试水一波。。。
难度感觉还行。
T1.买铅笔
题目描述
P老师需要去商店买n支铅笔作为小朋友们参加NOIP的礼物。她发现商店一共有3种包装的铅笔,不同包装内的铅笔数量有可能不同,价格也有可能不同。为了公平起见,P老师决定只买同一种包装的铅笔。
商店不允许将铅笔的包装拆开,因此P老师可能需要购买超过n支铅笔才够给小朋 友们发礼物。
现在P老师想知道,在商店每种包装的数量都足够的情况下,要买够至少n支铅笔最少需要花费多少钱。
输入输出格式
输入格式:
第一行包含一个正整数n,表示需要的铅笔数量。
接下来三行,每行用2个正整数描述一种包装的铅笔:其中第1个整数表示这种 包装内铅笔的数量,第2个整数表示这种包装的价格。
保证所有的7个数都是不超过10000的正整数。
输出格式:
1个整数,表示P老师最少需要花费的钱。
思路:
打卡签到题。贪心一波,5 min AC。
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#include<set>
#include<map>
#include<vector>
#include<fstream>
using namespace std;
int n;
int a[3][3];
inline int read()
{
char kr=0;
char ls;
for(;ls>'9'||ls<'0';kr=ls,ls=getchar());
int xs=0;
for(;ls>='0'&&ls<='9';ls=getchar())
{
xs=xs*10+ls-48;
}
if(kr=='-') xs=0-xs;
return xs;
}
int main()
{
n=read();
for(int i=1;i<=3;i++)
{
for(int j=1;j<3;j++)
{
a[i][j]=read();
}
}
int minn=99999999,cnt;bool ok;
for(int i=1;i<=3;i++)
{
ok=0;
if(n%a[i][1]) ok=1;
cnt=n/a[i][1]+ok;
minn=min(minn,a[i][2]*cnt);
}
printf("%d\n",minn);
return 0;
}
注:minn要尽量开大。
T2.回文日期
题目描述
在日常生活中,通过年、月、日这三个要素可以表示出一个唯一确定的日期。
牛牛习惯用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 1和date 2都是真实存在的日期,且年份部分一定为4位数字,且首位数字不为0。
保证date 1—定不晚于date 2。
输出格式:
一个整数,表示在date1和date2之间,有多少个日期是回文的。
思路:
也是水题一道,只要从date 1 到data 2枚举年月,判断是否回文,且是否位于指定的时间内,统计是回文的年份。(暴力枚举)
第一遍蒟丑的代码:
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#include<set>
#include<map>
#include<vector>
#include<fstream>
using namespace std;
string s1;
string s2;
int t1[8];
int t2[8];
inline int read()
{
char kr=0;
char ls;
for(;ls>'9'||ls<'0';kr=ls,ls=getchar());
int xs=0;
for(;ls>='0'&&ls<='9';ls=getchar())
{
xs=xs*10+ls-48;
}
if(kr=='-') xs=0-xs;
return xs;
}
int main()
{
int start=0,end=0;
cin>>s1>>s2;
for(int i=0;i<8;i++)
{
t1[i]=s1[i]-48;
t2[i]=s2[i]-48;
}
for(int i=0;i<4;i++)
{
start=start*10+t1[i];
end=end*10+t2[i];
}
int pd1=0,pd2=0;
for(int j=4;j<8;j++)
{
pd1=pd1*10+t1[j];
pd2=pd2*10+t2[j];
}
int tot=0;
int yue=0,ri=0;
for(int i=start;i<=end;i++)
{
yue=0;ri=0;
int pj=i%10;//第四位
int pi=(i/10)%100;//第三位
int po=(i/100)%10;//第二位
int pu=i/1000;//第一位
if(pj>1) continue;
yue=pj/10+pi;
ri=po*10+pu;
if(i==start)
{
if(yue*100+ri<pd1)
continue;
}
if(i==end)
{
if(yue*100+ri>pd2)
continue;
}
if(yue!=2)
{
if(pj==0)
{
tot++;
continue;
}
if(pj>0)
{
if(pi>=0&&pi<=2)
{
tot++;
continue;
}
}
}
if(yue==2)
{
bool ok=0;
if(!(start%4) && start%100)
{
ok=1;
}
else if(!(start%100))
{
if(!(start%400))
ok=1;
}
if(ok)
{
if(ri>0&&ri<30)
{
tot++;
continue;
}
}
else if(!ok)
{
if(ri>0&&ri<29)
{
tot++;
continue;
}
}
}
}
printf("%d\n",tot);
return 0;
}
优化后的代码:
#include<cstdio>
#include<algorithm>
#include<string>
#include<cstring>
#include<iostream>
#include<cmath>
#include<queue>
#include<stack>
#include<vector>
using namespace std;
int s[13]={0,31,29,31,30,31,30,31,31,30,31,30,31};
int main()
{
int ans=0;
string s1,s2;
int start=0,end=0;
cin>>s1>>s2;
for(int i=0;i<4;i++)
{
start=10*start+s1[i]-48;
end=10*end+s2[i]-48;
}
for(int i=1;i<=12;i++)
{
for(int j=1;j<=s[i];j++)
{
int nx=i*100+j;
int pi=nx%10;
int po=(nx/10)%10;
int pu=(nx/100)%10;
int pv=nx/1000;
int tot=pi*1000+po*100+pu*10+pv;
if(tot>=start&&tot<=end) ans++;
}
}
printf("%d\n",ans);
return 0;
}
T3.海港
题目描述
小K
是一个海港的海关工作人员,每天都有许多船只到达海港,船上通常有很多来自不同国家的乘客。
小K
对这些到达海港的船只非常感兴趣,他按照时间记录下了到达海港的每一艘船只情况;对于第i艘到达的船,他记录了这艘船到达的时间ti (单位:秒),船上的乘客数ki,以及每名乘客的国籍x
小K
统计了n艘船的信息,希望你帮忙计算出以每一艘船到达时间为止的24小时(24小时=86400秒)内所有乘船到达的乘客来自多少个不同的国家。
形式化地讲,你需要计算n条信息。对于输出的第i条信息,你需要统计满足ti−86400<tp≤ti的船只p,在所有的xp,j中,总共有多少个不同的数。
输入输出格式
输入格式:
第一行输入一个正整数n,表示小K
统计了n艘船的信息。
接下来n行,每行描述一艘船的信息:前两个整数ti和ki分别表示这艘船到达海港的时间和船上的乘客数量,接下来ki个整数,表示船上乘客的国籍xi,1,xi,2...xi,k。
保证输入的t_iti是递增的,单位是秒;表示从小K
第一次上班开始计时,这艘船在第t_iti秒到达海港。
保证 1≤n≤105,∑ki≤3∗105 ,1≤xi,j≤105, 1≤t(i−1)≤ti≤109。
其中∑ki表示所有的ki的和。
输出格式:
输出n行,第i行输出一个整数表示第i艘船到达后的统计信息。
思路:
①我个人的思路是开个结构体,存每一个人的进港的时间和所属的国籍,用一个变量nn记录一天中最早进港的那艘船的编号。(从0s开始标记船,每进一艘,就把这艘船打上标记(++cnt))。船进港后,统计人头数(国籍,时间)存入结构体,这一天内最早的那艘船开始暴力统计(过时的船就把在那艘船上的人的信息全部清空,更新nn),直接输出。(相当暴力)
②正解:也是记录每个人的进港时间和国籍(跟我的差不多),正解唯一精妙之处在于,在统计国籍的时候用了个叫队列的东西。没有那么暴力。(思路还是一样的)
暴力代码(70分):
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#include<set>
#include<map>
#include<vector>
#include<fstream>
using namespace std;
#define maxn 300007
#define day 86399
struct hh
{
int time,gg;
}t[maxn];
int n,cnt=0;
inline int read()
{
char kr=0;
char ls;
for(;ls>'9'||ls<'0';kr=ls,ls=getchar());
int xs=0;
for(;ls>='0'&&ls<='9';ls=getchar())
{
xs=xs*10+ls-48;
}
if(kr=='-') xs=0-xs;
return xs;
}
int main()
{
n=read();
int kk,sx,lck,tot=0;
int nn;
int tt[maxn];
for(int i=1;i<=n;i++)
{
kk=read();
if(i==1) nn=1;
sx=read();
for(int j=0;j<sx;j++)
{
++cnt;
t[cnt].time=kk;
t[cnt].gg=read();
}
tot=0;
memset(tt,0,sizeof(tt));
for(int j=nn;j<=cnt;j++)
{
if(t[j].time<(kk-day))
{
t[j].time=0;
t[j].gg=0;
nn=j+1;
continue;
}
if(!tt[t[j].gg])
{
tt[t[j].gg]=1;
tot++;
}
}
cout<<tot<<endl;
}
return 0;
}
正解:
#include <cstdio>
struct NOI
{
int tm,ct;
}a[300005];
int b[100005]; //b[i]记录【二十四小时内】有多少个国籍为i的人
int n,k,tot,time,i,j,tmp;
int ans,head;
int main(){
scanf("%d",&n);
for(i=1;i<=n;i++){
scanf("%d%d",&time,&k);
while(k--){
scanf("%d",&tmp); //读入人的同时,扔进队列
a[++tot].tm=time;
a[tot].ct=tmp;
if(b[tmp]==0)ans++; //新的品种,ans++
b[tmp]++;
}
while(time-a[head].tm>=86400){ //二十四小时前的,扔出队列
tmp=a[head].ct;
b[tmp]--;
if(b[tmp]==0)
ans--; //品种灭绝,ans--
head=head+1;
}
printf("%d\n",ans); //ans已经统计好种类数了
}
return 0;
}
T4.魔法阵
题目描述
六十年一次的魔法战争就要开始了,大魔法师准备从附近的魔法场中汲取魔法能量。
大魔法师有m个魔法物品,编号分别为1,2,...,m。每个物品具有一个魔法值,我们用Xi表示编号为i的物品的魔法值。每个魔法值Xi是不超过n的正整数,可能有多个物品的魔法值相同。
大魔法师认为,当且仅当四个编号为a,b,c,d的魔法物品满足xa<xb<xc<xd,Xb-Xa=2(Xd-Xc)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的物品的魔法值。
保证1≤n≤15000,1≤m≤40000,1≤Xi≤n。每个Xi是分别在合法范围内等概率随机生成的。
输出格式:
共m行,每行4个整数。第i行的4个整数依次表示编号为i的物品作为A,B,C,D物品分别出现的次数。
保证标准输出中的每个数都不会超过109。每行相邻的两个数之间用恰好一个空格隔开。
思路:
本题的题目这么显然,若是想要骗分,手打一个N4的暴力,35分就有了。
但是,如果要AC就需要动点脑子——
先观察题目里的三个条件:
Xa<Xb<Xc<Xd
Xb-Xa=2(Xd-Xc)
3(Xb-Xa)<(Xc-Xb)
第一个条件告诉我们,选择的四元组必须是一个严格递增的。
对于后两个条件,先转换成数学模型,不难发现其中Xd-Xc是最小的单位,所以我们可以令t=Xd-Xc,则整理式子后可以写成:
Xd-Xc=t
Xb-Xa=2t
Xc-Xb>6t
到现在为止我们找到了一组关于a,b,c,d的连续的关系,于是就可以计算了。
因为题目里的数字的范围比数字的数量要少,所以用桶排序,根据乘法原理,一个数要是出现了多次直接乘上就好了。
然后我们枚举长度tt,在分别枚举a,d的位置,当枚举了t和任意一个点的位置以后,其余的点根据上面的连续的关系都可以直接确定。
不难发现有一部分的计算是重复的,所以可以用一个变量记录一下前缀和,每次跟新以后直接加在后面。(就这样推理一下,就把N4的超级暴力优化为N2的AC)
暴力代码(35分):
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
#include<algorithm>
#include<queue>
#include<stack>
#include<deque>
#include<set>
#include<map>
#include<vector>
#include<fstream>
using namespace std;
#define maxn 45000
int n,m;
int sum[maxn];
struct hh
{
int a,b,c,d;
}t[maxn];
inline int read()
{
char kr=0;
char ls;
for(;ls>'9'||ls<'0';kr=ls,ls=getchar());
int xs=0;
for(;ls>='0'&&ls<='9';ls=getchar())
{
xs=xs*10+ls-48;
}
if(kr=='-') xs=0-xs;
return xs;
}
int main()
{
m=read();n=read();
for(int i=1;i<=n;i++)
{
sum[i]=read();
}
for(int i=1;i<=n;i++)//a
{
for(int j=1;j<=n;j++)//b
{
if(j==i) continue;
for(int k=1;k<=n;k++)//c
{
if(k==j||k==i) continue;
for(int l=1;l<=n;l++)//d
{
if(l==i||l==j||l==k) continue;
if(sum[i]>=sum[j] || sum[i]>=sum[k] ||sum[i]>=sum[l] ||sum[j]>sum[k] ||sum[j]>sum[l] || sum[k]>sum[l]) continue;
if(sum[j]-sum[i]==2*(sum[l]-sum[k]))
{
if((sum[j]-sum[i])*3<sum[k]-sum[j])
{
++t[i].a;
++t[j].b;
++t[k].c;
++t[l].d;
}
}
}
}
}
}
for(int i=1;i<=n;i++)
{
printf("%d %d %d %d\n",t[i].a,t[i].b,t[i].c,t[i].d);
}
return 0;
}
正解代码:
#include<cstdio> #include<cstring> #include<cstdlib> using namespace std; int n,m; int val[40010],num[15010]; int a[15010],b[15010],c[15010],d[15010]; int main()
{ scanf("%d%d",&n,&m); for(int i=1;i<=m;i++)
{ scanf("%d",&val[i]); num[val[i]]++; } for(int i=1;i*9+1<=n;i++)
{ int sum=0; for(int j=i*9+2;j<=n;j++)
{ sum+=num[j-7*i-1]*num[j-9*i-1]; c[j-i]+=num[j]*sum; d[j]+=num[j-i]*sum; } sum=0; for(int j=n-i*9-1;j>=1;j--)
{ sum+=num[j+i*9+1]*num[j+i*8+1]; a[j]+=num[j+2*i]*sum; b[j+2*i]+=num[j]*sum; } } for(int i=1;i<=m;i++)
{ printf("%d %d %d %d\n",a[val[i]],b[val[i]],c[val[i]],d[val[i]]); }
return 0;
}
自测:
用时:3.5h+一节课。
score:100+100+70+35=305
传送门