noip普及组解题报告.docx
《noip普及组解题报告.docx》由会员分享,可在线阅读,更多相关《noip普及组解题报告.docx(17页珍藏版)》请在冰点文库上搜索。
![noip普及组解题报告.docx](https://file1.bingdoc.com/fileroot1/2023-6/23/6ab5a485-2291-41d5-879d-c3e5ce402779/6ab5a485-2291-41d5-879d-c3e5ce4027791.gif)
noip普及组解题报告
NOIP2015普及组(Junior)解题报告
1. 金币
(coin.cpp/c/pas)
国王将金币作为工资,发放给忠诚的骑士。
第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币……;这种工资发放模式会一直这样延续下去:
当连续N天每天收到N枚金币后,骑士会在之后的连续N+1天里,每天收到N+1枚金币。
请计算在前K天里,骑士一共获得了多少金币。
【输入格式】
输入文件名为coin.in。
输入文件只有1行,包含一个正整数K,表示发放金币的天数。
【输出格式】
输出文件名为coin.out。
输出文件只有1行,包含一个正整数,即骑士收到的金币数。
【输入输出样例1】
coin.in
coin.out
6
14
见选手目录下的coin/coin1.in和coin/coin1.ans。
【输入输出样例1说明】
骑士第一天收到一枚金币;第二天和第三天,每天收到两枚金币;第四、五、六天,每天收到三枚金币。
因此一共收到1+2+2+3+3+3=14枚金币。
【输入输出样例2】
coin.in
coin.out
1000
29820
见选手目录下的coin/coin2.in和coin/coin2.ans。
【数据说明】
对于100%的数据,1≤K≤10,000。
【思路】
枚举。
【代码】
ViewCode
2.扫雷游戏
(mine.cpp/c/pas)
扫雷游戏是一款十分经典的单机小游戏。
在n行m列的雷区中有一些格子含有地雷
(称之为地雷格),其他格子不含地雷(称之为非地雷格)。
玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。
游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。
现在给出n行m列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。
注:
一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。
【输入格式】
输入文件名为mine.in。
输入文件第一行是用一个空格隔开的两个整数n和m,分别表示雷区的行数和列数。
接下来n行,每行m个字符,描述了雷区中的地雷分布情况。
字符’*’表示相应格子是地雷格,字符’?
’表示相应格子是非地雷格。
相邻字符之间无分隔符。
【输出格式】
输出文件名为mine.out。
输出文件包含n行,每行m个字符,描述整个雷区。
用’*’表示地雷格,用周围的地雷个数表示非地雷格。
相邻字符之间无分隔符。
【输入输出样例1】
mine.in
mine.out
33*?
?
?
?
?
?
*?
*10
221
1*1
见选手目录下的mine/mine1.in和mine/mine1.ans。
【输入输出样例2】
mine.in
mine.out
23?
*?
*?
?
2*1
*21
见选手目录下的mine/mine2.in和mine/mine2.ans。
【输入输出样例3】
见选手目录下的mine/mine3.in和mine/mine3.ans。
【数据说明】 对于100%的数据,1≤n≤100,1≤m≤100。
【思路】
枚举+统计。
【代码】
ViewCode
3. 求和
(sum.cpp/c/pas)
一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。
每个格子上都染了一种颜色(用[1,m]当中的一个整数表示),并且写了一个数字number。
𝑚𝑏𝑒𝑟𝑖
定义一种特殊的三元组:
(x,y,z),其中 x,y,z 都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:
1. 𝑥, 𝑦, 𝑧都是整数, 𝑥 < 𝑦 < 𝑧, 𝑦−𝑥 = 𝑧−𝑦
2. 𝑐𝑜𝑙𝑜𝑟𝑥= 𝑐𝑜𝑙𝑜𝑟𝑧
满足上述条件的三元组的分数规定为(x+z) ∗ (𝑛𝑢𝑚𝑏𝑒𝑟𝑥+ 𝑛𝑢𝑚𝑏𝑒𝑟𝑧)。
整个纸带的分数规定为所有满足条件的三元组的分数的和。
这个分数可能会很大,你只要输出整个纸带的分数除以 10,007 所得的余数即可。
【输入格式】
输入文件名为 sum.in。
第一行是用一个空格隔开的两个正整数𝑛和𝑚,𝑛代表纸带上格子的个数,𝑚代表纸带上颜色的种类数。
第二行有𝑛个用空格隔开的正整数,第𝑖个数字𝑛𝑢𝑚𝑏𝑒𝑟𝑖代表纸带上编号为𝑖的格子上面写的数字。
第三行有𝑛个用空格隔开的正整数,第𝑖个数字𝑐𝑜𝑙𝑜𝑟𝑖代表纸带上编号为𝑖的格子染的颜色。
【输出格式】
输出文件名为 sum.out。
共一行,一个整数,表示所求的纸带分数除以 10,007 所得的余数。
【输入输出样例 1】
sum.in
sum.out
62
553222
221121
82
见选手目录下的 sum/sum1.in 和 sum/sum1.ans。
【输入输出样例 1 说明】
纸带如题目描述中的图所示。
所有满足条件的三元组为:
(1,3,5),(4,5,6)。
所以纸带的分数为(1+5) ∗ (5+2)+(4+6) ∗ (2+2)=42+40=82。
【输入输出样例 2】
sum.in
sum.out
154
5108222997756424
223343324444111
1388
见选手目录下的 sum/sum2.in 和 sum/sum2.ans。
【输入输出样例 3】
见选手目录下的 sum/sum3.in 和 sum/sum3.ans。
【数据说明】
对于第 1 组至第 2 组数据,1≤ 𝑛 ≤100,1≤ 𝑚 ≤5;
对于第 3 组至第 4 组数据,1≤ 𝑛 ≤3000,1≤ 𝑚 ≤100;对于第 5 组至第 6 组数据,1≤ 𝑛 ≤100000,1≤ 𝑚 ≤100000,且不存在出现次数超过 20 的颜色;对于全部 10 组数据, 1≤ 𝑛 ≤100000,1≤ 𝑚 ≤100000,1≤𝑐𝑜𝑙𝑜𝑟𝑖≤ 𝑚,1≤𝑛𝑢𝑚𝑏𝑒𝑟𝑖≤100000。
【思路】
扫描法(扫描法即枚举+维护)。
暴力枚举:
相同颜色且相同奇偶性的两个格子。
时间为O(n^2)。
前缀和思想应用:
对于一个格子i,设之前出现的与之同奇偶p同颜色ci的格子数目为s[ci][p]、格子序号和为sr[ci][p]、格子num值之和为sa[ci][p]、格子序号与a之积之和为sq[ci][p],则如下累计ans即可:
注意s>1才应该累计。
(详见代码)
【代码】
1#include
2#defineFOR(a,b,c)for(inta=(b);a<=(c);a++)
3usingnamespacestd;
4
5constintmaxn=100000+10;
6constintMOD=10007;
7
8typedeflonglongLL;
9LLn,m;
10LLa[maxn],c[maxn],sr[maxn][2],sa[maxn][2],s[maxn][2],sq[maxn][2];
11//MOD用int出过错索性改为longlong
12
13intmain(){
14freopen("sum.in","r",stdin);
15freopen("sum.out","w",stdout);
16scanf("%lld%lld",&n,&m);
17FOR(i,1,n)scanf("%lld",&a[i]);
18FOR(i,1,n)scanf("%lld",&c[i]);
19LLans=0;
20FOR(i,1,n){
21intp=i&1,ci=c[i];
22s[ci][p]++;
23
24if(s[ci][p]>1)
25ans+=(a[i]*sr[ci][p])%MOD+(i*sa[ci][p])%MOD+sq[ci][p]+((s[ci][p]-1)*i*a[i]%MOD);
26ans%=MOD;
27
28sr[ci][p]+=i;
29sa[ci][p]+=a[i];
30sq[ci][p]+=i*a[i];
31}
32ans%=MOD;
33printf("%lld\n",ans);
34return0;
35}
4. 推销员
(salesman.cpp/c/pas)
【问题描述】
阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。
螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。
螺丝街一共有N家住户,第i家住户到入口的距离为Si 米。
由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。
阿明会从入口进入,依次向螺丝街的X家住户推销产品,然后再原路走出去。
阿明每走1米就会积累1点疲劳值,向第i家住户推销产品会积累Ai 点疲劳值。
阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。
【输入格式】
输入文件名为salesman.in。
第一行有一个正整数N,表示螺丝街住户的数量。
接下来的一行有N个正整数,其中第i个整数Si 表示第i家住户到入口的距离。
数据保证S1≤S2≤…≤Sn<108。
接下来的一行有N个正整数,其中第i个整数Ai 表示向第i户住户推销产品会积累的疲劳值。
数据保证Ai<103。
【输出格式】
输出文件名为salesman.out。
输出N行,每行一个正整数,第i行整数表示当X=i时,阿明最多积累的疲劳值。
【输入输出样例1】
salesman.in
salesman.out
5
12345
12345
15
19
22
24
25
见选手目录下的salesman/salesman1.in和salesman/salesman1.ans。
【输入输出样例1说明】
X=1:
向住户5推销,往返走路的疲劳值为5+5,推销的疲劳值为5,总疲劳值为15。
X=2:
向住户4、5推销,往返走路的疲劳值为5+5,推销的疲劳值为4+5,总疲劳值为5+5+4+5=19。
X=3:
向住户3、4、5推销,往返走路的疲劳值为5+5,推销的疲劳值3+4+5,总疲劳值为5+5+3+4+5=22。
X=4:
向住户2、3、4、5推销,往返走路的疲劳值为5+5,推销的疲劳值2+3+4+5,总疲劳值5+5+2+3+4+5=24。
X=5:
向住户1、2、3、4、5推销,往返走路的疲劳值为5+5,推销的疲劳值1+2+3+4+5,总疲劳值5+5+1+2+3+4+5=25。
【输入输出样例2】
salesman.in
salesman.out
5
12245
54341
12
17
21
24
27
见选手目录下的salesman/salesman2.in和salesman/salesman2.ans。
【输入输出样例2说明】
X=1:
向住户4推销,往返走路的疲劳值为4+4,推销的疲劳值为4,总疲劳值4+4+4=12。
X=2:
向住户1、4推销,往返走路的疲劳值为4+4,推销的疲劳值为5+4,总疲劳值4+4+5+4=17。
X=3:
向住户1、2、4推销,往返走路的疲劳值为4+4,推销的疲劳值为5+4+4,总疲劳值4+4+5+4+4=21。
X=4:
向住户1、2、3、4推销,往返走路的疲劳值为4+4,推销的疲劳值为5+4+3+4,总疲劳值4+4+5+4+3+4=24。
或者向住户1、2、4、5推销,往返走路的疲劳值为5+5,推销的疲劳值为5+4+4+1,总疲劳值5+5+5+4+4+1=24。
X=5:
向住户1、2、3、4、5推销,往返走路的疲劳值为5+5,推销的疲劳值为5+4+3+4+1,总疲劳值5+5+5+4+3+4+1=27。
【样例输入输出3】
见选手目录下的salesman/salesman3.in和salesman/salesman3.ans。
【数据说明】对于20%的数据,1≤N≤20; 对于40%的数据,1≤N≤100; 对于60%的数据,1≤N≤1000; 对于100%的数据,1≤N≤100000。
【思路】
贪心+线段树。
可以知道每次增加人数得到的答案都是在前一个答案的基础上挑选了一家最优的客户。
贪心地看,我们每次应该选择劳累度增加值最大的。
对于每一次查询而言,设目前为止出现的最远客户的下标为srank,同时定义sa[i]=2*s[i]+a[i],我们在srank之左的客户中选出a值最大的,在srank之右选出sa值最大的,将sa减去2*s[srank]之后比较得当前最大增加值,并相应设最大值为0。
以上操作均可以通过维护两棵线段树在O(logn)时间内完成,因此总的时间为O(nlogn)。
(应该会有更简单的方法)
【代码】
1#include
2#include
3#include
4#defineFOR(a,b,c)for(inta=(b);a<=(c);a++)
5usingnamespacestd;
6
7constintmaxn=400000+10;
8constintINF=1e9;
9
10structNode{
11intmx,mr;
12Node(){mx=-INF;}
13};
14structSegment_Tree{
15intn;
16intt[maxn],maxv[maxn],maxr[maxn];
17
18voidbuild(intu,intL,intR){
19intlc=u*2,rc=u*2+1;
20if(L==R){
21maxv[u]=t[L];
22maxr[u]=L;
23}
24else{
25intM=L+(R-L)/2;
26build(lc,L,M);
27build(rc,M+1,R);
28if(maxv[lc]>maxv[rc]){
29maxv[u]=maxv[lc];
30maxr[u]=maxr[lc];
31}
32else{
33maxv[u]=maxv[rc];
34maxr[u]=maxr[rc];
35}
36}
37}
38voidinit(intn,int*a){
39this->n=n;
40FOR(i,1,n)t[i]=a[i];
41build(1,1,n);
42}
43inty1,y2;
44Nodequery(intu,intL,intR){
45intlc=u*2,rc=u*2+1;
46if(y1<=L&&R<=y2){
47Nodeans;
48ans.mr=maxr[u],ans.mx=maxv[u];
49returnans;
50}
51else{
52intM=L+(R-L)/2;
53Nodeans,tmp;
54if(y1<=M){
55tmp=query(lc,L,M);
56if(tmp.mx>ans.mx)ans=tmp;
57}
58if(M59tmp=query(rc,M+1,R);
60if(tmp.mx>ans.mx)ans=tmp;
61}
62returnans;
63}
64}
65intx;
66voidAdd(intu,intL,intR){
67intlc=u*2,rc=u*2+1;
68if(L==R){
69maxv[u]=0;
70maxr[u]=L;
71}
72else{
73intM=L+(R-L)/2;
74if(x<=M)Add(lc,L,M);
75elseAdd(rc,M+1,R);
76if(maxv[lc]>maxv[rc]){
77maxv[u]=maxv[lc];
78maxr[u]=maxr[lc];
79}
80else{
81maxv[u]=maxv[rc];
82maxr[u]=maxr[rc];
83}
84}
85}
86}A,B;
87
88ints[maxn],sa[maxn],sb[maxn];
89intn;
90
91intread(){
92charc=getchar();
93while(!
isdigit(c))c=getchar();
94intx=0;
95while(isdigit(c)){
96x=x*10+c-'0';
97c=getchar();
98}
99returnx;
100}
101intmain(){
102freopen("salesman.in","r",stdin);
103freopen("salesman.out","w",stdout);
104
105n=read();
106FOR(i,1,n)s[i]=read();
107FOR(i,1,n)sb[i]=read();
108FOR(i,1,n)sa[i]=2*s[i]+sb[i];
109
110A.init(n,sa),B.init(n,sb);
111
112intsrank=0,ans=0;
113FOR(i,1,n){
114inta,b;
115if(srank==0)a=0,b=1;
116elsea=srank-1,b=srank+1;
117Nodet1,t2;
118if(a){
119B.y1=1,B.y2=a;
120t1=B.query(1,1,n);
121}
122if(b<=n){
123A.y1=b,A.y2=n;
124t2=A.query(1,1,n);
125t2.mx=sb[t2.mr]+2*(s[t2.mr]-s[srank]);
126}
127intansl=0,ansr=0;
128if(t1.mx!
=-INF)ansl=sb[t1.mr];
129if(t2.mx!
=-INF)ansr=t2.mx;
130Noderes;
131if(ansl>ansr)res=t1;elseres=t2,srank=res.mr;
132ans+=res.mx;
133printf("%d\n",ans);
134A.x=B.x=res.mr;
135A.Add(1,1,n),B.Add(1,1,n);
136}
137return0;
138}