2014年蓝桥杯(第5届)预赛本科B组C语言真题解析

2014年蓝桥杯(第5届)预赛本科B组C语言真题解析
2014年蓝桥杯(第5届)预赛本科B组C语言真题解析

2014 年蓝桥杯(第5 届)预赛本科B 组真题解析

啤酒和饮料

啤酒每罐 2.3 元,饮料每罐 1.9 元。小明买了若干啤酒和饮料,一共花了 82.3 元。我们还知道他买的啤酒比饮料的数量少,请你计算他买了几罐啤酒。注意:答案是一个整数。请通过浏览器提交答案。

不要书写任何多余的内容(例如:写了饮料的数量,添加说明文字等) 。

(1)答案。

11

( 2)编程思路。

把啤酒、饮料的单价和总共花的钱都乘上10,转换为整数。然后用循环对啤酒的罐数

b(1≤b<823/23)和饮料的罐数 c(1≤c<823/19 )进行穷举,找出满足要求的啤酒罐数b。

(3)源程序。

#include

int main()

{

int b,c;

for (b=1;b<35;b++)

for (c=1;c<45;c++)

{

if((823==b*23+c*19) &&(b

printf("%d\n",b);

}

return 0;

}

(4)用单重循环完成穷举。实际上,也可以只对啤酒的罐数进行穷举。

#include

int main()

{

int b,cSum;

for (b=1;b<35;b++)

{

cSum=823-23*b;

if (cSum%19==0 && cSum/19>b)

printf("%d\n",b);

} return 0;

}

切面条

一根高筋拉面,中间切一刀,可以得到 2 根面条。

如果先对折 1 次,中间切一刀,可以得到 3 根面条。如果连续对折 2 次,中间切一刀,可以得到 5 根面条。那么,连续对折 10 次,中间切一刀,会得到多少面条呢?答案是个整数,请通过浏览器提交答案。不要填写任何多余的内容。

(1)答案。

1025

( 2)编程思路。

由题目可知,对折 0 次切一刀得到 2 根,对折 1 次切一刀得到 3 根,对折 2 次切一刀得到 5 根。自己再尝试对折 3 次切一刀,发现可以得到 9 根。

设 F[i] 表示对折 i 次后中间切一刀得到的面条根数,则有 F[i]=2*F[i -1]-1

( i≥1)。

(3)源程序。

#include

int main()

{

int f[11]={2,3,5};

for (int i=3;i<=10;i++){

f[i]=2*f[i -1]-1;

} printf("%d\n",f[10]); return 0;

}

李白打酒

话说大诗人李白,一生好饮。幸好他从不开车。一天,他提着酒壶,从家里出来,酒壶中有酒 2 斗。他边走边唱:

无事街上走,提壶去打酒。

逢店加一倍,遇花喝一斗。

这一路上,他一共遇到店 5次,遇到花 10 次,已知最后一次遇到的是花,他正好把酒喝光了。

请你计算李白遇到店和花的次序,可以把遇店记为 a,遇花记为 b。则:babaabbabbabbbb 就是合理的次序。像这样的答案一共有多少呢?请你计算出所有可能方案的个数(包含题目

给出的)。

注意:通过浏览器提交答案。答案是个整数。不要书写任何多余的内容。(1)答案。

14

( 2)编程思路。

采用递归的方法生成 15 位二进制数的排列,对每一种排列进行判断,看是否满足要求遇到店 5 次、酒正好喝完、最后一次遇到的是花) ,满足条件输出对应字符串即可。

(3)源程序。

#include

int cnt=0;

void dfs(int *a,int k)

{

if (k==15)

{

int alcohol,drinkery,i;

alcohol=2; drinkery=0;

for (i=0;i<15;i++)

if (a[i]==0)

{ alcohol=2*alcohol; drinkery++;

}

else

alcohol -- ;

if (alcohol==0 && drinkery==5 && a[14]==1)

{

for (i=0;i<15;i++)

if (a[i]==0) printf("a");

else printf("b");

printf("\n");

cnt++;

}

return;

}

a[k]=0;

dfs(a,k+1);

a[k]=1;

dfs(a,k+1);

}

int main()

{

int a[15];

dfs(a,0);

printf("Count=%d\n",cnt);

return 0;

}

( 4)程序优化。

上面的程序中用二进制数 0表示遇到店, 1 表示遇到花。实际上,可以直接定义一个字

符数组 char a[16] ,该数组的元素 a[0]~a[14] 直接用字符' a'或'b' 赋值, a[15]赋结束符' \0'。

另外,通过递归产生 15 位二进制数的排列,共 215=32768 种情况,也就是上面的程序搜索判断了 32768 种情况。实际上,由于满足问题的解是遇店 5次,因此在某一搜索过程中遇店达到 6 次,肯定不是问题的解,无需继续进行,可以实施剪枝。

为了实施剪枝,需要在递归时记录酒的斗数和遇店的次数,可以改写函数为

void dfs(char *a,int k,int alcohol,int drinkery) ,其中 alcohol 记录酒壶里酒的斗数, drinkery 记录遇店的次数。

剪枝优化后的源程序如下:

#include

int cnt=0;

void dfs(char *a,int k,int alcohol,int drinkery)

{

if (k==15)

{

if (alcohol==0 && drinkery==5 && a[14]=='b')

{

printf("%s\n",a);

cnt++;

}

return;

}

if (drinkery<=5 && alcohol!=0) // 剪枝

{

a[k]='a'; dfs(a,k+1,2*alcohol,drinkery+1); a[k]='b';

dfs(a,k+1,alcohol -1,drinkery);

}

}

int main()

{

char a[16];

a[15]='\0';

dfs(a,0,2,0);

printf("Count=%d\n",cnt);

return 0;

}

史丰收速算

史丰收速算法的革命性贡献是:从高位算起,预测进位。不需要九九表,彻底颠覆了传

统手算 !

速算的核心基础是: 1 位数乘以多位数的乘法。 其中,乘以 7 是最复杂的,就以它为例。

因为, 1/7 是个循环小数: 0.142857...,如果多位数超过 同理, 2/7, 3/7, ... 6/7 也都是类似的循环小数,多

位数超过 下面的程序模拟了史丰收速算法中乘以 7 的运算

过程。 乘以 7 的个位规律是:偶数乘以 2,奇数乘以 2 再

加 5 乘以 7 的进位规律是: 满 142857... 进 1, 满 285714... 进 2, 满 428571... 进 3, 满 571428... 进 4, 满 714285... 进 5, 满 857142... 进 6 请分析程序流程,填写划线部分缺少的代码。

//计算个位

int ge_wei(int a)

{

if(a % 2 == 0)

return (a * 2) % 10;

else

return (a * 2 + 5) % 10;

}

//计算进位

int jin_wei(char* p)

{

char* level[] = { "142857", "285714", "428571", "571428", "714285", "857142" };

char buf[7]; buf[6] = '\0'; strncpy(buf,p,6);

int i;

for(i=5; i>=0; i --){

int r = strcmp(level[i], buf); if(r<0) return i+1; while(r==0){

p += 6; strncpy(buf,p,6); r = strcmp(level[i], buf); if(r<0) return

i+1; ______________________________ ; // 填空 }

}

return 0;

}

//多位数乘以 7

void f(char* s)

{

int head = jin_wei(s);

if(head > 0) printf("%d", head);

char* p = s;

while(*p){

int a = (*p -'0');

int x = (ge_wei(a) + jin_wei(p+1)) % 10;

printf("%d",x);

p++;

142857...,就要进

1 n/7 ,就要进 n 都只取个位。

}

printf("\n");

}

int main()

{

f("428571428571"); f("34553834937543"); return 0;

} 注意:通过浏览器提交答案。只填写缺少的内容,不要填写任何多余的内容(例如:说明性文字)

(1)参考答案。

if(r>0) return i

(2)解析。

leve[i] 就相当于满 leve[i] 进 i+1 ,因为填空所在的那段是判断条件为r==0 的循环,所以

是在当前 buff 段与某个 leve 相等的情况下,看下一个 buff 段进位多少,那么 r<0 就是进位 i+1 , r>0 就是进位 i,r==0 就继续看下一段 buff 。

打印图形

小明在 X 星球的城堡中发现了如下图形和文字:

小明开动脑筋,编写了如下的程序,实现该图形的打印。

#define N 70

void f(char a[][N], int rank, int row, int col) {

if(rank==1){ a[row][col] = '*';

return;

}

int w = 1;

int i;

for(i=0; i

f(a, rank -1, row+w/2, col); f(a, rank -1, row+w/2, col+w);

}

int main()

{

char a[N][N]; int i,j;

for(i=0;i

f(a,6,0,0);

for(i=0; i

for(j=0; j

}

return 0;

} 请仔细分析程序逻辑,填写缺失代码部分。

通过浏览器提交答案。注意不要填写题目中已有的代码。也不要写任何多余内容(比如说明性的文字)

(1)参考答案。

f(a, rank- 1, row, col+w/2);

奇怪的分式

上小学的时候,小明经常自己发明新算法。一次,老师出的题目是:

1/4 乘以 8/5

小明居然把分子拼接在一起,分母拼接在一起,答案是: 18/45 (参见图 1.png)

老师刚想批评他,转念一想,这个答案凑巧也对啊,真是见鬼!对于分子、分母都是 1~9 中的一位数的情况,还有哪些算式可以这样计算呢?请写出所有不同算式的个数(包括题中举例的)。

显然,交换分子分母后,例如: 4/1 乘以 5/8 是满足要求的,这算做不同的算式。但对于分子分母相同的情况, 2/2 乘以 3/3 这样的类型太多了,不在计数之列 ! 注意:答案是个整数(考虑对称性,肯定是偶数)。请通过浏览器提交。不要书写多余的内容。

(1)答案。

14

( 2)编程思路。

将分式符号化为: a/b × c/d = (10a+c)/(10b+d。)对 a、b、c、d 在 1~9 之间取值穷举即可。穷举时, a!=b 且 c!=d 。

(3)源程序。

#include

int main()

{

int cnt=0;

for(int a=1;a<=9;a++)

{

for(int b=1;b<=9;b++)

{

if(b==a) continue; for(int c=1;c<=9;c++){

for(int d=1;d<=9;d++)

{

if(d==c) continue;

if(a*c*(b*10+d)==b*d*(a*10+c)) cnt++;

}

}

}

}

printf("%d\n",cnt);

return 0;

}

六角填数

如图【 1.png 】所示六角形中,填入 1~12 的数字。

使得每条直线上的数字之和都相同。

图中,已经替你填好了 3 个数字,请你计算星号位置所代表的数字是多少?请通过浏览器提交答案,不要填写多余的内容。

(1)答案。

10

( 2)编程思路。

定义一个数组 int a[13] ,其中 a[1]~a[12]这 12个元素分别从上到下,自左向右的 12个圆圈。可采用递归的方法生成由 1~12这 12个数字组成的无重复数字的全排列。

生成 1~12 不同的 12个数字存储到数组中后,检测 6 条直线上的四个数字之和是否全相等,如果全相等,是一组解,输出并计数。

(3)源程序。

#include

int a[13];

int vis[13];

int cnt=0;

void dfs(int x)

{

if(x == 12)

{

int t[6];

t[0] = a[1] + a[3] + a[6] + a[8];

t[1] = a[1] + a[4] + a[7] + a[11];

t[2] = a[2] + a[3] + a[4] + a[5];

t[3] = a[2] + a[6] + a[9] + a[12];

t[4] = a[8] + a[9] + a[10] + a[11];

t[5] = a[12] + a[10] + a[7] + a[5];

for(int i = 1; i < 6; ++i)

{

if(t[i] != t[i -1]) return ;

}

cnt++;

printf("No %d\n",cnt);

printf("%12d\n",a[1]); printf("%3d%6d%6d%6d\n",a[2],a[3],a[4],a[5]);

printf("%6d%12d\n",a[6],a[7]);

printf("%3d%6d%6d%6d\n",a[8],a[9],a[10],a[11]);

printf("%12d\n\n",a[12]);

return ;

}

for(int i = 1;i < 13; ++i)

{

if(!vis[i])

{

vis[i] = 1; a[x] = i;

dfs(x+1);

vis[i] = 0;

}

}

}

int main()

{

for (int i=1;i<=12;i++)

vis[i]=0;

vis[1] = 1;

a[1] = 1;

vis[8] = 1; a[2] = 8;

vis[3] = 1;

a[12] =3;

dfs(3);

printf("Count=%d\n",cnt); return 0;

}

蚂蚁感冒

长 100 厘米的细长直杆子上有 n 只蚂蚁。它们的头有的朝左,有的朝右。每只蚂蚁都只能沿着杆子向前爬,速度是1厘米 /秒。

当两只蚂蚁碰面时,它们会同时掉头往相反的方向爬行。

这些蚂蚁中,有 1 只蚂蚁感冒了。并且在和其它蚂蚁碰面时,会把感冒传染给碰到的蚂蚁。

请你计算,当所有蚂蚁都爬离杆子时,有多少只蚂蚁患上了感冒。【数据格式】第一行输入一个整数 n (1 < n < 50), 表示蚂蚁的总数。

接着的一行是 n个用空格分开的整数 Xi (-100 < Xi < 100), Xi 的绝对值,表示蚂蚁离开杆子左边端点的距离。正值表示头朝右,负值表示头朝左,数据中不会出现 0 值,也不会出现两只蚂蚁占用同一位置。其中,第一个数据代表的蚂蚁感冒了。

要求输出 1 个整数,表示最后感冒蚂蚁的数目。例如,输入:

3

5 -2 8 程序应输出:

1再例如,输入:

5

-10 8 -20 12 25 程序应输出:

3 ( 1)编程思路。首先,两只蚂蚁相遇各自反向可以看作是两只蚂蚁分别穿过对方继续

前进。这样处理的

话就简单多了。感冒蚂蚁不管初始方向朝哪一边,它右边的蚂蚁只要向左走就可能碰撞感染(特殊情况除外),同样,它左边的蚂蚁只要朝右边走也可能被感染。

假如感冒蚂蚁开始时向左行,则会感染它左边所有向右行的蚂蚁(因为相遇后它穿过对

方继续向左行碰到向右行的蚂蚁感染它们);相遇被感染的第 1 只蚂蚁也穿过继续向右行,感染所有它右边向左行的蚂蚁。

感冒蚂蚁开始时向右行情况类似。这样,最后感冒的蚂蚁的数量为:感冒蚂蚁左边蚂蚁向右走的数量 + 右边蚂蚁向左走的数量 + 感冒蚂蚁本身特殊情况是,当感冒蚂蚁向左爬的时候,如果感冒蚂蚁左边没有向右爬行的蚂蚁,那么不管感冒蚂蚁右边有多少向左爬行的,因爬行的速度相同感冒蚂蚁不会遇到向右爬的蚂蚁进

行感染,因此右边的蚂蚁也永远不可能被感染。

同样的,当感冒蚂蚁向右爬的时候,如果感冒蚂蚁右边没有向左爬行的蚂蚁,那么同样感冒蚂蚁左边的蚂蚁也永远不可能被感染。

(2)源程序。

#include

int abs(int x)

{

return x>=0?x: -x;

}

int main()

{

int ant[51];

int n,i;

scanf("%d",&n);

for (i=0;i

int left=0,right=0;

for (i=1;i

{

if (ant[i]<0 && abs(ant[i])>abs(ant[0])) left++; // 感冒蚂蚁右边且向左

走的 if (ant[i]>0&&abs(ant[i])

向右走的

}

if ((ant[0]<0 && right==0)|| (ant[0]>0 && left==0)) printf("1\n");

else printf("%d\n",left+right+1);

return 0;

}

地宫取宝

X 国王有一个地宫宝库。是 n x m 个格子的矩阵。每个格子放一件宝贝。每个宝贝贴着价值标签。

地宫的入口在左上角,出口在右下角。小明被带到地宫的入口,国王要求他只能向右或向下行走。

走过某个格子时,如果那个格子中的宝贝价值比小明手中任意宝贝价值都大,小明就可

以拿起它(当然,也可以不拿) 。

当小明走到出口时,如果他手中的宝贝恰好是 k 件,则这些宝贝就可以送给小明。请你帮小明算一算,在给定的局面下,他有多少种不同的行动方案能获得这 k 件宝贝。【数据格式】

输入一行 3 个整数,用空格分开: n m k (1<=n,m<=50, 1<=k<=12)

接下来有 n 行数据,每行有 m 个整数 Ci (0<=Ci<=12)代表这个格子上的宝物的价值

要求输出一个整数,表示正好取 k 个宝贝的行动方案数。该数字可能很大,输出它对1000000007 取模的结果。

例如,输入:

2 2 2

1 2

2 1

程序应该输出:

2

再例如,输入:

2 3 2

1 2 3

2 1 5

程序应该输出:

14

( 1)编程思路。

采用记忆化搜索完成。

定义 4维数组 dp[51][51][15][15] 。设 dp[x][y][num][val] 表示在坐标(x,y)时拿了num 件宝贝并且宝贝中价值最大的为 val,其中 1≤x≤n,1≤ y≤ m, num的初值为 0,表示还没有拿到宝贝, val 的初值本来应该为 -1,表示此时手上还没有宝物(因为从题目数据说明中可以看出宝贝的价值可以为 0),为了让 val 初始值为 0,可以将输入的宝贝的价值统一加

1,这

样宝贝的最小价值为 1(不是 0)。

定义二维数组 int map[51][51] 保存地宫各格子的宝贝价值。采用倒推法列出状态转移方程,即把后面的情况种数不断的往前更新。当 map[x][y]>val 时,

dp[x][y][num][val]=dp[x+1][y][num+1][map[x][y]]+dp[x][y+1][num+1][map[x][y]]

+dp[x+1][y][num][val]+dp[x][y+1][num][val] ;

当map[x][y]<=val 时,dp[x][y][num][val]=dp[x+1][y][num][val]+dp[x][y+1][num][val] 。

在通过 DFS 搜索方式求数组 dp 的各元素值时,由于数组元素值 dp[x][y][num][val] 跟位置( x,y )、宝贝个数以及当前最大的宝贝价值有关,当重复遍历这个结点时,若 dp[x][y][num][val] 的值已经计算出来了,则直接应用无需重复递归计算。为此,定义数组dp 的全部元素的初始值为-1。若计算时需要用到dp[x][y][num][val] ,此时dp[x][y][num][val] ! =-1,则无需重复调用,直接应用计算好的 dp[x][y][num][val] 元素值。之所以初值定义为 -1,是考虑到若路径不存在的情况(此时方案数应为0)。

(2)源程序。

#include

#include

#define MOD 1000000007

long long dp[51][51][15][15];

int map[51][51];

int n,m,k;

void dfs(int x, int y, int num, int val)

{

if (dp[x][y][num][val] != -1)

return;

dp[x][y][num][val] = 0;

if (x == n && y == m && num == k)

{

dp[x][y][num][val] = 1;

return;

}

if (map[x][y] > val && num < k)

{

dfs(x, y, num + 1, map[x][y]);

dp[x][y][num][val] += dp[x][y][num + 1][map[x][y]];

dp[x][y][num][val] %= MOD;

}

if (x < n) // 向下走

{

dfs(x + 1, y, num, val);

dp[x][y][num][val] += dp[x + 1][y][num][val];

dp[x][y][num][val] %= MOD;

}

if (y < m) // 向右走

{

dfs(x, y + 1, num, val);

dp[x][y][num][val] += dp[x][y + 1][num][val];

dp[x][y][num][val] %= MOD;

}

}

int main()

{

scanf("%d%d%d",&n,&m,&k);

for (int i = 1; i <=n ; i++)

{

for (int j = 1; j <= m; j++)

{

scanf("%d",&map[i][j]); map[i][j]++;

}

}

memset(dp, -1, sizeof(dp));

dfs(1, 1, 0, 0);

printf("%d\n",dp[1][1][0][0]);

return 0;

}

小朋友排队

n 个小朋友站成一排。现在要把他们按身高从低到高的顺序排列,但是每次只能交换位

置相邻的两个小朋友。

每个小朋友都有一个不高兴的程度。开始的时候,所有小朋友的不高兴程度都是0。

如果某个小朋友第一次被要求交换,则他的不高兴程度增加 1,如果第二次要求他交换,则他的不高兴程度增加 2(即不高兴程度为 3),依次类推。当要求某个小朋友第 k 次交换时,他的不高兴程度增加 k。

请问,要让所有小朋友按从低到高排队,他们的不高兴程度之和最小是多少。如果有两个小朋友身高一样,则他们谁站在谁前面是没有关系的。

【数据格式】

输入的第一行包含一个整数 n,表示小朋友的个数。

第二行包含 n 个整数 H1 H2 ? Hn,分别表示每个小朋友的身高。输出一行,包含一个整数,表示小朋友的不高兴程度和的最小值。例如,输入:

3

3 2 1

程序应该输出:

9

【样例说明】

首先交换身高为 3 和 2 的小朋友,再交换身高为 3 和 1 的小朋友,再交换身高为 2 和 1 的小朋友,每个小朋友的不高兴程度都是3,总和为 9。

【数据规模与约定】

对于 10%的数据, 1<=n<=10 ;

对于 30%的数据, 1<=n<=1000 ;

对于 50%的数据, 1<=n<=10000 ;

对于 100%的数据, 1<=n<=100000 ,

0<=Hi<=1000000 。

( 1)编程思路。

本题的实质是求一组数据中逆序数对的个数。比如题目中的3,2,1,和 3 有关的逆序对为(3,2)和(3,1),和 2 有关的逆序对为(3,2)和(2,1),和 1有关的逆序对为(3,

1)和( 2,1),为了完成排序,任何一个逆序对的两个元素都必须交换一次,于是可知,每个小朋友都完成了两次交换。

要求一个数组元素有关的逆序对个数,就是求它之前有几个大于它的元素(设为

b1 ),

之后有几个小于它的元素(设为b2),这样每个元素的交换次数为 b1+b2。

根据数据规模与约定,若采用二重循环进行暴力搜索逆序对的个数,肯定会超时的。因此,采用树状数组来解决本题。

树状数组实际上是由两部分组成:数据数组(设为num)和统计数组(设为 C)。

我们以数据数组 num[3]={3,2,1} 为例进行描述。树状数组的各元素初始值为 0,但由于

树状数组元素下标是从 1 开始的,因此将数据数组的每个元素值加1,然后作为树状数组的

下标,将数值 1 存到相应的位置。

从左到右依次扫描数据数组。

1)读入 3,此时读入的数据个数为 1,树状数组 C 为:

C[1] C[2] C[3] C[4] C[5] C[6] C[7] C[8] ??

0 0 0 1 0 0 0 0

可以看到 sum(C[1],C[4])=1 ,这是小于等于 3 的数字的个数,也就是说当输入第一个数3的时候没有比它小的数字存在。这时“输入数值个数 -sum(C[1],C[4])=1 -1=0”,也就是说大于 3 的数字的个数为 0,保存起来,即 b[0]=0 。( 在这里特别注意,根据树状数组

sum(C[1],C[n]) 算出来的数值是某个数字 n 左边比它小的数字的个数。 )

2)读入 2,此时读入的数据个数为 2,树状数组 C 为:

C[1] C[2] C[3] C[4] C[5] C[6] C[7] C[8] ??

0 0 1 1 0 0 0 0

可以看到 sum(C[1],C[3])=1 ,仍然不存在比它小的数,而此时输入的数据个数为 2,2-1=1 ,就是说,存在一个数在 2 之前并且大于 2(这个数是第 1 个数 3),保存 b[1]=1 。

3)读入 1,此时读入的数据个数为 3,树状数组 C 为:

C[1] C[2] C[3] C[4] C[5] C[6] C[7] C[8] ??

0 1 1 1 0 0 0 0

可以看到 sum(C[1],C[2])=1 ,仍然不存在比它小的数,而此时输入的数据个数为 3,3-1=2 ,就是说,存在两个数在 1 之前并且大于 1(这两个数就是 3 和 2),保存 b[2]=2 。

到此,求出了每个数据元素前面的较大的数的个数了。

类似地,从右到左依次扫描数据数组,求出每个数据元素后面的较小的数的个数。此时依次直接求 sum(C[1],C[n]) ,得到每个元素后面的较小的数的个数,然后将得到的数值累加到相应的 b[i]中。最终我们会得到 b[0]=2 ,b[1]=2 ,b[2]=2 ,分别对应 num[0]=3 ,

num[1]=2 , num[2]=1 。

另外需要注意的是,如果输入数组中出现重复的数字怎么办?如果出现,可以简单地处理。具体方法是通过树状数组求得sum(1,a)和 sum(1,a+1) ,其中输入的数字为 a,前者算出的小于 a 的数的个数,后者算出的是小于等于 a(小于 a+1)的数的个数,两个相减就是等

于 a 的个数。

采用树状数组求得每个小朋友被移动的次数后,需要计算其不高兴程度。若小朋友被移

动 n 次,则其不高兴程度为 1+2+3+ ? +n。为避免重复计算,实现构造好 total 数组,数组元素 total[n] 的值就是 1+2+3+ ?+n。这样在计算不高兴程度总和时直接引用元素值即可。

(2)源程序。

#include

#include

#define MAX 1000010

#define N 100010

int C[MAX],b[MAX];

int num[N];

long long total[N],ans;

int lowbit(int x) // 取出 x 的最低位 1 所在位号

{

return x&( -x);

}

void add(int pos,int num,int *C)

{

while(pos

{

C[pos]+=num; pos+=lowbit(pos);

}

}

int Sum(int pos,int *C)

{

int sum=0; while(pos>0)

{ sum+=C[pos]; pos-=lowbit(pos);

}

return sum;

}

int main()

{

int i; total[0]=0;

for (i=1;i

{ total[i]=total[i -1]+i;

} memset(C,0,sizeof(C));

int n; scanf("%d",&n);

for (i=0;i

{ scanf("%d",&num[i]); add(num[i]+1,1,C); b[i]=i -Sum(num[i],C);

b[i]=b[i] -(Sum(num[i]+1,C) - Sum(num[i],C) -1); } memset(C,0,sizeof(C));

for(i=n -1;i>=0;i --)

{ add(num[i]+1,1,C); b[i]=b[i]+Sum(num[i],C);

} ans=0;

for(i=0;i

{ ans+=total[b[i]];

} printf("%I64d\n",ans);

return 0;

相关主题
相关文档
最新文档