[1104]2012年南京市复赛模拟测试题2

本文解析了2012年南京市信息学奥林匹克竞赛复赛模拟试题,涉及统计差异数、树顶点标号、分班算法及区间最大值查询等问题,提供了详细的算法分析和参考代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2012年南京市复赛模拟测试题(普及)

题目

统计差异数

树顶点标号

分班

联络

程序名(*. pas/c/cpp)

dec

labeling

orz

biu

输入文件

dec.in

labeling.in

orz.in

biu.in

输出文件

dec.out

labeling.out

orz.out

biu.out

用时

1s

1s

1s

1s

内存限定

128M

128M

128M

128M

 

第一题:统计差异数(dec)

【问题描述】

国家统计局每年都对各城市的人的收入进行调查统计。针对不同人群之间收入差异度进行的统计。一般统计方法是:对不同人群收入进行求差,符合规定差值的数目统计出来。

【输入格式】

   第一行包括2个非负整数N(不同的人群数)和C(差值),中间用空格隔开。

   第二行有N个整数(每个人群的收入),中间用空格隔开。

【输出格式】

   输出一行,表示人群差值为C的数对的个数。

【输入样例】
4 1
1 1 2 3

【输出样例】

   3

【数据规模】

   对于90%的数据,N <= 2000;

   对于100%的数据,N <= 200000。

   所有输入数据都在longint范围内。

【算法分析】

算法1:朴素算法

枚举A数与B数,把A-B=C数对都找出来,很明显,对于n<=200000的数据这种做法必定会超时。
算法2:二分查找
先不考虑一个数字重复多次出现的情况,对于一个数A,又已知与A的差为C,也就是说当A和C确定下来的时候,另一个数B的大小也就确定下来了。所以我们可以第一遍枚举A,然后再在剩下的数中找确定的B。
由于B的值已经确定,可以用二分查找来降低复杂度。具体做法如下:
先对n个数进行排序,排序后统计相同数字出现的次数(也可以把相同的数字都缩成一个数字,并记录下总数)。然后枚举n个数,再对排完序后的n个数进行二分查找,并累计答案。
这样O(n^2)就可以降为O(n log n)。 
算法3:队列
换一种思路来减少要判断的数对。对于A-B=C而言,如果A-B>C,很显然对于同一个A,不需要再查找更小的B;如果A-B<C,同理对于同一个B,不需要再找更小的A。得到如下算法: 
1、对n个数排序,排序后统计相同数字出现的次数,把相同的数字都缩成一个数字,并记录下总数;
2、视排序后的数队同时为A队和B队,指针都指向第一个元素;
3、若指针所指的A-B>C,即B不能再减小了,那么A队的指针就往后移一位,减小A值;若指针所指的A-B<C,即A不能再减小了,那么B队的指针就后移一位,减小B值;
4、若有A-B=C,那么就在答案上增加当前的数对个数,注意加入相同数的个数。 

此题无标程,我就不说我直接暴力+优化的,看看题解肯定就能写出来的- -,就不放我的程序了 。

第二题:树顶点标号(Labeling)

【问题描述】

   Maximum最近学习了有关多叉树的知识。如果存在这样一颗树,其所有的叶子节点的深度都相同,并且所有非叶子节点都有K个子节点,我们就称这颗树为满K叉树。显然,如果我们知道K和这颗满K叉树的深度d,我们很容易就能计算出这颗树的节点总数,不妨记为N。

Maximum突然想出了这样一个问题:假设我们对这些节点进行1至N标号。如果在一种标号方案中,任意一个节点的标号都大于其所有祖先节点的标号,我们就称当前的标号方案为合法的。

 

那么,如果已知K和d,一共有多少种合法的标号方案呢?Maximum请你编程帮他算一算。

【输入格式】

   输入文件中包含两个数字K和d(根高度为零),意义如上文所述。

【输出格式】

   输出文件中应该仅有一个数字,表示合法的标号方案个数。

【输入样例】

2 2

【输出样例】

80

【数据规模】

1 ≤ K * d ≤ 21

    【算法分析】

  推推公式,应该可以得到以下结论ans=n!/(∏f[i])

其中n为节点总数,f[i]为节点i的子树的节点数
然后分解质因数,求求高精度即可。(OrzTJG)

      【标程】 

 1 var
 2 
 3         k , d, i, j, z ,q ,ws : integer;
 4         jw :int64;
 5         a : array1..6000 ] of integer;
 6         x, y : array0..21 ] of integer;
 7         ans : array1..9180 ] of 0..9;
 8 begin
 9         assign( input, 'labeling.in' ); reset( input );
10         assign( output, 'labeling.out' ); rewrite( output );
11         readln( k, d );
12         x[ 0 ] := 1;
13         for i := 1 to d do
14                 x[ i ] := x[ i - 1 ] * k + 1;
15         y[ d ] := 1;
16         for i := d - 1 downto 1 do
17                 y[ i ]:= y[ i + 1 ] * k;
18         dec( x[ d ] );
19         for i := 1 to x[ d ] do
20                 a[ i ] := i;
21         dec( d );
22         for i := 1 to d do
23         while x [ i ] <>1 do begin
24                 for q := 2 to x[ i ] do
25                 while x[ i ] mod q = 0 do begin
26                         x[ i ] := x[ i ] div q;
27                         for j := 1 to y[ i ] do
28                         begin
29                                 z := x[ d + 1 ];
30                                 while a[ z ] mod q <>0 do dec( z );
31                                 a[ z ] := a[ z ] div q;
32                         end;
33                 end;
34         end;
35         inc( d );
36         fillchar( ans, sizeof( ans ), 0 );
37         ans[ 1 ] := 1; ws := 1;
38         for i := 1 to x[ d ] do
39         begin
40                 jw := 0;
41                 for j := 1 to ws do begin
42                         jw := jw + ans[ j ] * a[ i ];
43                         ans[ j ]:= jw mod 10;
44                         jw := jw div 10;
45                 end;
46                 while jw <> 0 do begin
47                         inc( ws );
48                         ans[ ws ] := jw mod 10;
49                         jw := jw div 10;
50                 end;
51         end;
52         for i := ws downto 1 do
53                 write( ans[ i ] );
54         writeln;
55         close( input ); close( output );
56 end.

 

第三题: 分班(orz

【问题描述】新学期马上就要开学了,学校让小憨为新生进行分班。因考虑上好体育课,分班的原则比较特别,要么保证整个班级都是同一性别,要么男女性别人数差不超过M。另外,现在N位同学排成一排,小憨只会把连续一段的同学分进一个班级。学校想知道,至少需要分出多少个班级。

【输入格式】

输入文件第一行包括N和M。

之后N行,每行一个整数,1表示男生,2表示女生。

【输出格式】

输出一个整数,表示最小分出班级的数量。

【输入样例】

5 1

2

2

1

2

2

【输出样例】

2

【数据规模】

对于30%的数据,有1 ≤ N ,M≤ 50

对于100%的数据,有1 ≤ N,M ≤ 2500

      【算法分析】

每个集合里的数在原数串中都是连续的一段,采用类似区间动态规划的思想,将[1,i]区间分成[1,j]区间与合法[j+1,i]集合组成,则要使[1,i]区间满足条件的集合数最小,显然[1,j]区间满足条件的集合数为最小,即每一段的最小值都可以从前面的某一段的最小值转移过来。
每次考虑对于[1,i]这段数,决策是从前面的所有数段[1,j](j<i)中找一个段。满足:
1、第J+1个数到第I个满足组成一个集合的条件;
2、第1个到第J个数所组成的集合总数最少。
则段[1,i]的最小集合数为:第1个到第J个数所组成的集合总数的基础上加1。
分析组成一个集合的条件,题目已经告诉我们,一个集合中要么只有1或只有2,要么1和2的个数之差不超过M。 
预处理,设 s1[i],s2[i]分别表示区间[1,I]中1的个数与2的个数。用O(N)的代价求得。
区间[i,j]中:
1的个数S1=s1[j]-s1[i-1];
2的个数S2=s2[j]-s2[i-1];
这样,只要S1=0 或S2=0或ABS(S1-S2)<=M,就能组成集合。
设F[i]表示第1个到第I个数所组成的最少集合数量。 
动态转移方程:F[I]=MIN{F[J]}+1{S1[J+1,I]=0 or S2[J+1,I]=0 or ABS(S1[J+1,I]-S2[J+1,I])<=M}

 【标程】

 1 var
 2     q,p,i,j,n,m:longint;
 3     qq,pp,s,a:array[0..3000]of longint;
 4     c:char;
 5 procedure refresh(i,j:longint);
 6 begin
 7     if (a[i+j]>a[i]+1)or(a[i+j]=-1then a[i+j]:=a[i]+1;
 8 end;
 9 begin
10     assign(input,'orz10.in');reset(input);
11     assign(output,'orz10.out');rewrite(output);
12     readln(n,m);
13     for i:=1 to n do 
14         begin
15             readln(c);
16             if c='1' then s[i]:=1 else s[i]:=2;
17         end;
18     for i:=1 to n do
19         begin
20             qq[i]:=qq[i-1];
21             pp[i]:=pp[i-1];
22             if s[i]=1 then inc(qq[i]) else inc(pp[i]);
23         end;
24     fillchar(a,sizeof(a),255);
25     a[0]:=0;
26     for i:=0 to n-1 do
27         for j:=1 to n-i do
28             if (qq[j+i]-qq[i]=0)or(pp[j+i]-pp[i]=0)or(abs(qq[j+i]-qq[i]-pp[j+i]+pp[i])<=m) then refresh(i,j);
29     writeln(a[n]);
30     close(input);
31     close(output);
32 end.
33     

 

 

第四题:天才的记忆(Talentm)

【问题描述】

从前有个人名叫W and N and B,他有着天才般的记忆力,他珍藏了许多许多的宝藏。在他离世之后留给后人一个难题(专门考验记忆力的啊!),如果谁能轻松回答出这个问题,便可以继承他的宝藏。

题目是这样的:给你一大串数字(编号为1到 N,大小可不一定哦!),在你看过一遍之后,它便消失在你面前,随后问题就出现了,给你M个询问,每次询问就给你两个数字A,B,要求你瞬间就说出属于A到B这段区间内的最大数。一天,一位美丽的姐姐从天上飞过,看到这个问题,感到很有意思(主要是据说那个宝藏里面藏着一种美容水,喝了可以让这美丽的姐姐更加迷人),于是她就竭尽全力想解决这个问题。BUT,她每次都以失败告终,因为这数字的个数是在太多了!于是她请天才的你帮他解决。如果你帮她解决了这个问题,可是会得到很多甜头的哦!

 

【输入格式】

一个整数N表示数字的个数,接下来一行为N个数。第三行读入一个M,表示你看完那串数后需要被提问的次数,接

下来M行,每行都有两个整数A,B。

【输出格式】

输出共M行,每行输出一个数。

【输入样例】

6

34 1 8 123 3 2

4

1 2

1 5

3 4

2 3

【样例输出】

34

123

123

8

【数据规模】

对于30%的数据,1<=N<=10000,1<=M<=100

对于100%的数据,1<=N<=200000,1<=M<=10000.

 

【算法分析】

经典的RMQ问题,亦可用线段树解决,是VIJOS上的原题,类似于TYVJ忠诚。

【我的程序】

 

 1 var m,n,i,j,h,min:longint;
 2     a,b,x,y:array[1..200000of longint;
 3 procedure sortquick(l,r:longint);
 4 var i,j,x,y:longint;
 5 begin
 6   i:=l;j:=r;
 7   x:=a[(l+r) div 2];
 8   repeat
 9     while a[i]>x do inc(i);
10     while x>a[j] do dec(j);
11     if i<=j then
12     begin
13       y:=a[i];
14       a[i]:=a[j];
15       a[j]:=y;
16       y:=b[i];
17       b[i]:=b[j];
18       b[j]:=y;
19       inc(i);
20       dec(j);
21     end;
22   until i>j;
23   if i<r then sortquick(i,r);
24   if l<j then sortquick(l,j);
25 end;
26 begin
27   assign(input,'Talentm.in');
28   reset(input);
29   assign(output,'Talentm.out');
30   rewrite(output);
31 
32 
33   readln(m);
34   for i:=1 to m do begin read(a[i]); b[i]:=i; end;
35   readln(n);
36   for i:=1 to n do readln(x[i],y[i]);
37   sortquick(1,m);
38   for j:=1 to n-1 do
39    for i:=1 to m do
40     if (b[i]>=x[j]) and (b[i]<=y[j]) then
41      begin
42        writeln(a[i]);
43        break;
44      end;
45   for i:=1 to m do
46    if (b[i]>=x[n]) and (b[i]<=y[n]) then
47     begin
48       writeln(a[i]);
49       break;
50     end;
51    close(output);
52    close(input);
53 end.

 

【标程】

 

 

 1 const maxn=200000;
 2       maxlogn=17;
 3  
 4 var RMQ:array[0..maxn,0..maxlogn]of longint;
 5     log2:array[1..maxn]of longint;
 6     n,m:longint;
 7  
 8 function max(a,b:longint):longint;
 9 begin
10   if a>b then
11     max:=a
12   else
13     max:=b;
14 end;
15  
16 procedure main;
17 var i,j,k,l:longint;
18 begin
19   assign(input,'Talentm.in');
20   assign(output,'Talentm.out');
21   reset(input);
22   rewrite(output);
23   readln(n);
24   for i:=1 to n do read(RMQ[i,0]);
25   k:=0;
26   for i:=1 to n do
27   begin
28     if 1 shl (k+1)=i then inc(k);
29     log2[i]:=k;
30   end;
31   k:=log2[n];
32   for j:=1 to k do
33     for i:=1 to n-(1 shl j)+1 do
34       RMQ[i,j]:=max(RMQ[i,j-1],RMQ[i+1 shl (j-1),j-1]);
35   readln(m);
36   for l:=1 to m do
37   begin
38     read(i,j);
39     k:=log2[j-i+1];
40     writeln(max(RMQ[i,k],RMQ[j-(1 shl k)+1,k]));
41   end;
42   close(input);
43   close(output);
44 end;
45  
46 begin
47   main;
48 end.

转载于:https://www.cnblogs.com/shy-/archive/2012/11/04/2754116.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值