当前位置:文档之家› NOIP普及组复赛解题报告

NOIP普及组复赛解题报告

NOIP2015 普及组解题报告南京师范大学附属中学树人学校CT1.金币(coin.cpp/c/pas)【问题描述】国王将金币作为工资,发放给忠诚的骑士。

第一天,骑士收到一枚金币;之后两天(第二天和第三天),每天收到两枚金币;之后三天(第四、五、六天),每天收到三枚金币;之后四天(第七、八、九、十天),每天收到四枚金币;这种工资发放模式会一直这样延续下去:当连续N天每天收到N枚金币后,骑士会在之后的连续N+1天里,每天收到N+1枚金币。

请计算在前K 天里,骑士一共获得了多少金币。

【输入格式】输入文件名为coin.in 。

输入文件只有1行,包含一个正整数K,表示发放金币的天数。

【输出格式】输出文件名为coin.out。

输出文件只有1 行,包含一个正整数,即骑士收到的金币数。

【数据说明】对于100%的数据,1 < K < 10,000【思路】模拟【时空复杂度】O(k) ,O(1)2 、扫雷游戏(mine.cpp/c/pas )【问题描述】扫雷游戏是一款十分经典的单机小游戏。

在n行m列的雷区中有一些格子含有地雷(称之为地雷格),其他格子不含地雷(称之为非地雷格)。

玩家翻开一个非地雷格时,该格将会出现一个数字——提示周围格子中有多少个是地雷格。

游戏的目标是在不翻出任何地雷格的条件下,找出所有的非地雷格。

现在给出n行m列的雷区中的地雷分布,要求计算出每个非地雷格周围的地雷格数。

注:一个格子的周围格子包括其上、下、左、右、左上、右上、左下、右下八个方向上与之直接相邻的格子。

【输入格式】输入文件名为mine.in 。

输入文件第一行是用一个空格隔开的两个整数n和m分别表示雷区的行数和列数。

接下来n行,每行m个字符,描述了雷区中的地雷分布情况。

字符’* '表示相应格子是地雷格,字符' ?'表示相应格子是非地雷格。

相邻字符之间无分隔符。

【输出格式】输出文件名为mine.out 。

输出文件包含n行,每行m个字符,描述整个雷区。

用’* '表示地雷格,用周围的地雷个数表示非地雷格。

相邻字符之间无分隔符。

【数据说明】对于100% 的数据,1< n W 1Q01< me 100【思路】模拟【技巧】可将数组多开一圈,省去边界条件的判断【时空复杂度】O(mn),O(mn)3. 求和(sum.cpp/c/pas)【问题描述】一条狭长的纸带被均匀划分出了n个格子,格子编号从1到n。

每个格子上都染了一种颜色color i(用[1,m]当中的一个整数表示),并且写了一个数字number i。

定义一种特殊的三元组:(x, y, z),其中x,y,z都代表纸带上格子的编号,这里的三元组要求满足以下两个条件:1.x, y, z 者E是整数,x < y < 乙y ? x = z ? y2.color x=color z满足上述条件的三元组的分数规定为(x+z)*(number x+number z)。

整个纸带的分数规定为所有满足条件的三元组的分数的和。

这个分数可能会很大,你只要输出整个纸带的分数除以10,007所得的余数即可。

【输入格式】输入文件名为sum.in。

第一行是用一个空格隔开的两个正整数n 和m,n 代表纸带上格子的个数,m 代表纸带上颜色的种类数。

第二行有n个用空格隔开的正整数,第i个数字number代表纸带上编号为i的格子上面写的数字。

第三行有m个用空格隔开的正整数,第i个数字color i代表纸带上编号为i的格子染的颜色。

【输出格式】输出文件名为sum.out。

共一行,一个整数,表示所求的纸带分数除以10,007所得的余数。

【数据说明】对于第1组至第2组数据,i<n<ioo,im^5对于第3组至第4组数据,i<n<3000,im x 100对于第5组至第6组数据,1<n< 100000,1m w 100000且不存在出现次数超过20 的颜色;对于全部10 组数据,1W n <100000, 1<m <100000, 1 <color i<m, 1 W n umber<100000【思路】先分析一下,我们的任务是什么。

题目的要求是求分数和,我们就得把所有符合条件的三元组“找”出来。

至少需要枚举三元组(x,y,z)中的一个元素,这里枚举的是z (当然x也可以,不过不要选y,因为y对分数没什么用)。

1、因为x<y<z,所以只需向前枚举x,y2、因为y-x二z-y,所以x+z=2y,x、z同奇偶,且分数与y无关,只需枚举z 和x。

3、因为colour x=colour z,所以只需枚举z之前同奇偶且同色的X。

这样的话时间复杂度是O(n2),能得40分。

如何快速枚举x呢?其实不是快速枚举x,是快速枚举分数和。

观察三元组分数:(x+z) • (number x+ number z)显然我们不方便处理多项式乘法,那就把它拆开(事实上很多人到这步都放弃了,其实试一试立刻就明白了)=x • number x+x • number z+z • numbei x+z • number z那么对于z的所有合法决策x1,x2, (x)根据乘法分配率,分数二艺(xi*number xi)+ 艺(xi)*number z + 艺(number x i) *z+ 艺(z*number z) (1<=i<=k)由于z是枚举的,所以只需快速得到艺(x • number x),艺x,艺number x和k (注意最后一项被算了k 次,要乘k)这样我们就可以开4 个累加器,分别记录这四个量。

而对于不同奇偶性、不同颜色的z有不同的决策x,所以要开一个s[2][m][4]的累加器。

【时空复杂度】O(n), O(n+m)【注意】题目数据较大,每次计算一定要模10007,否则很容易出错【样例程序】#include <cstdio>const int maxn=100000;const int maxm=100000;const int p=10007;int n,m,ans;int number[maxn+1],colour[maxn+1];int s[2][maxm+1][4];void init(){freopen("sum.in","r",stdin);freopen("sum.out","w",stdout);scanf("%d%d",&n,&m);for(int i=1;i<=n;i++)scanf("%d",&number[i]);for(int i=1;i<=n;i++)scanf("%d",&colour[i]);}void solve(){for(int i=1;i<=n;i++){int z=i%p,numz=number[i]%p,c=colour[i],t=i%2; intcount=s[t][c][0]%=p,x=s[t][c][1]%=p,numx=s[t][c][2]%=p,x_numx=s[t][c][3]%=p;ans=(ans+((count*z)%p*numz)%p)%p;ans=(ans+x_numx)%p;ans=(ans+x*numz)%p;ans=(ans+z*numx)%p;s[t][c][0]++;s[t][c][1]+=z;s[t][c][2]+=numz;s[t][c][3]+=z*numz;}}void output(){printf("%d\n",ans); fclose(stdin); fclose(stdout); }int main(){init(); solve(); output();return 0;4.推销员(salesman.cpp/c/pas)【问题描述】阿明是一名推销员,他奉命到螺丝街推销他们公司的产品。

螺丝街是一条死胡同,出口与入口是同一个,街道的一侧是围墙,另一侧是住户。

螺丝街一共有N 家住户,第i 家住户到入口的距离为S i 米。

由于同一栋房子里可以有多家住户,所以可能有多家住户与入口的距离相等。

阿明会从入口进入,依次向螺丝街的X 家住户推销产品,然后再原路走出去。

阿明每走1米就会积累1点疲劳值,向第i家住户推销产品会积累A i点疲劳值。

阿明是工作狂,他想知道,对于不同的X,在不走多余的路的前提下,他最多可以积累多少点疲劳值。

【输入格式】输入文件名为salesman.in。

第一行有一个正整数N,表示螺丝街住户的数量。

接下来的一行有N 个正整数,其中第i 个整数S i 表示第i 家住户到入口的距离。

数据保证S i <S<-<n S108。

接下来的一行有N 个正整数,其中第i 个整数A i 表示向第i 户住户推销产品会积累的疲劳值。

数据保证A i<103。

【输出格式】输出文件名为salesman.ou。

输出N 行,每行一个正整数,第i 行整数表示当X=i 时,阿明最多积累的疲劳值。

【数据说明】对于20%的数据,1< N< 20对于40%的数据,1< N< 100对于60%的数据,1< N< 1000对于100%的数据,1< N< 10000。

【思路】题目要求每一个X 的情况,显然不能每个X 专门推一遍,要充分利用已知的X 的情况,那么很可能会是DP 。

定义f[i] 为X=i 时的最大疲劳值。

关键是怎么建立状态转移方程呢?考试时观察了两组样例数据,直觉告诉我f[i+1]的决策应该会包含f[i]的决策(此处的决策指住户下标)。

事实上也确实如此。

证明:设f[i]的决策为也局,……,k i(k1< k2<……< k i), f[i+1]的决策将f[i]决策中的k s换成j并增加了一个决策k i+1, f[i+1]的决策k中最大的为maxk。

f[i]=2*s[k i]+ 艺a[k t](1<=t<=i)f[i+1]=2*s[maxk]+ 艺a[k t](1<=t<=s-1)+ 艺a[k t](s+1<=t<=i)+a[j]+a[k i+1]••• 2*s[maxk]+ 艺a[k t](1<=t<=s-1)+ 艺a[k t](s+1<=t<=i)+a[j]是X=i 时的一种决策的疲劳值(即决策为k1,k2, ....... k s-1,k s+1, .... k i,k j 时)二2*s[maxk]+ 艺a[k t](1<=t<=s-1)+ 艺a[k t](s+1<=t<=i)+a[j]<=f[i]二2*s[maxk]+ 艺a[k t](1<=t<=s-1)+ 艺a[k t](s+1<=t<=i)+a[j]+ a[k i+1] <=f[i]+ a[k M](即决策为k1,k2,……,k s,……,k i,k i+1时的疲劳值)若小于,说明保留k s更优;若等于,对于两个目前疲劳值相等的决策序列k,max{k} 越小越好(就是说目前走的路程越短越好),因为在max{k}左边的决策I只能增加a[l]的疲劳值,而对于max{k} 右边的决策r 则可以增加2*(s[r]-s[max{k}])+a[r] ,对于左边,max{k} 没有影响,而对于右边,max{k} 越小,后面的f[] 增加疲劳值的空间越大。

相关主题