CODEVS-新斯诺克

原题地址:新斯诺克

题目描述 Description

斯诺克又称英式台球,是一种流行的台球运动。在球桌上,台面四角以及两长边中心位置各有一个球洞,使用的球分别为1 个白球,15 个红球和6 个彩球(黄、绿、棕、蓝、粉红、黑)共22个球。

击球顺序为一个红球、一个彩球直到红球全部落袋,然后以黄、绿、棕、蓝、粉红、黑的顺序逐个击球,最后以得分高者为胜。斯诺克的魅力还在于可以打防守球,可以制造一些障碍球使对方无法击打目标球而被扣分。正是因为这样,斯诺克是一项充满神奇的运动。

现在考虑这样一种新斯诺克,设母球(母球即是白球,用于击打其他球)的标号为M,台面上有N 个红球排成一排,每一个红球都有一个标号,他们的标号代表了他们的分数。

现在用母球击打这些红球,一杆击打,如果母球接触到红球,就称为“K 到红球”。我们假设,一次可以击打任意多相邻连续的红球,也可以只击打一个球。并且红球既不会落袋,也不会相互发生碰撞,而只是停留在原处。每次击打时候,要想“K 到红球”,至少要击打一个红球,如果想一次击打多个红球,那么击打的红球必须是依次连续排列的。如果一次“K 到红球”所有红球的标号之和的平均数大于母球的标号M,就获得了一个“连击”。

现在请你计算总共能有多少种“连击”方案。

注意:如果当前有标号为1、2、3 的三种红球,母球标号为0,有如下6 种获得“连击”方案:( 1)、( 2)、( 3)、( 1,2)、( 2,3)、( 1,2,3)

输入描述 Input Description

共有两行。

第一行是N,M (N<=100000,M<=10000) ,N 表示台面上一共有N 个红球,M 表示母球的标号。

第二行是N 个正整数,依次表示台面上N 个红球的标号,所有标号均不超过10000。

输出描述 Output Description

只有一个数,为“连击”的方案总数。

样例输入 Sample Input

4 3

3 7 2 4

样例输出 Sample Output

7

输出情况:

(7),(2),(4),(3,7),(3,4),(3,7,2),(3,2,4),(3,7,2,4)

解题思路:

首先,此题抽象来看是想从一组数中找出所有的顺序对,且顺序对的平均数大于某一个数,统计有多少个这样的数。按照这个思路想到的是分治法中的归并排序思想,不过我先对这题做些化简,把所有红球的标号减去母球的标号,之后我只需要看是否大于0即可知道是否满足条件,还有一个地方注意一下,标号可以是负数,所以用一个read()函数进行处理,数组下标从1开始,其他下标值先置为0,方便之后比较,接着再将数组进行前缀和运算(数组a[ i ] = 当前数 - 母球数 + a[ i - 1 ]) ,记住数组是从1开始存的,得出的数组通过归并排序函数中比较一下并统计顺序对即可算出最终个数。

 

 代码如下:

 1 #include<cstdio>
 2 #include<iostream>
 3 using namespace std;
 4 
 5 int n, m, a[100002], b[100002];
 6 long long ans;
 7 
 8 int read() {
 9     //x是计算位数用的,f是判断正负号
10      int x = 0, f = 1; char ch = getchar();
11     while (ch<'0' || ch>'9') 
12     { 
13         if (ch == '-') 
14             f = -1; 
15         ch = getchar(); 
16     }
17     while (ch >= '0'&&ch <= '9') 
18     { 
19         x = x * 10 + ch - '0'; 
20         ch = getchar(); 
21     }
22     return x * f;
23 }
24 
25 void Merge_sort(int l, int r) {
26     if (l >= r) 
27         return;
28     int mid = (l + r) / 2;
29     Merge_sort(l, mid);
30     Merge_sort(mid + 1, r);
31     int i = l, j = mid + 1, k = l;
32     while (i <= mid && j <= r) {
33         if (a[i] < a[j]) 
34         {
35             b[k++] = a[i++];
36             ans += r - j + 1;//统计顺序对,(r-j+1)算出的结果就是当前条件下的顺序对
37         }
38         else b[k++] = a[j++];
39     }
40     while (i <= mid) 
41         b[k++] = a[i++];
42     while (j <= r) 
43         b[k++] = a[j++];
44     for (int w = l; w <= r; w++) 
45         a[w] = b[w];
46 }
47 
48 int main() {
49     n = read(); m = read();
50     for (int i = 1; i <= n; i++) {
51         a[i] = read() - m + a[i - 1];//求前缀和
52     }
53     Merge_sort(0, n);
54     cout << ans << endl;
55     return 0;
56 }

 可能我的描述还不清楚,可以结合代码自己演算或者调试一下,要先理解题目的要求和了解归并排序和前缀和的思想,不然会比较吃力,当然还有其他算法(听说树状数组可以做,暂时不会( ̄_ ̄|||))可以解决此题,大家可以借鉴我的做法


 

 

 

 

 

 

 

posted @ 2019-01-26 14:07  Diligent_Memory  阅读(202)  评论(0编辑  收藏  举报