您的位置:首页 > 其它

【基础练习】【归并逆序对】codevs3324 新斯诺克题解

2015-10-24 08:32 239 查看
题目描述 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

这道题目要求我们求出有多少种连续的数,其平均数大于m

换句话说,就是有多少种连续的数,和大于m*i

那么我们可以用一个前缀和数组做 从i到j的和可以表示为s[j]-s[i-1] 

那么我们求的是:对于任意i和j,有多少能满足i<j&&s[j]-s[i]>m(j-i)

这个式子实质求的是顺序对,其实只不过是统计的地方变了一下而已,但脑子容易晕

如果我们不愿意求顺序对,也可以更简单的求逆序对 原式可化简为:i<j && m*i-s[i]>m*j-s[j]

这样 我们令a[i]=m*i-s[i] 就可以直接做逆序对了

标准代码最好还是这一种,安全稳定

//codevs3324 ÐÂ˹ŵ¿Ë ¹é²¢ÄæÐò¶Ô
//copyright by ametake
#include
#include
#include

#define ll long long
using namespace std;

const ll maxn=100000+10;
ll a[maxn],b[maxn],s[maxn];
ll n,m;
ll tot=0;

void msort(ll l,ll r)
{
ll mid = l + r >> 1;
if (l0
{
msort(l,mid);
msort(mid+1,r);
}
ll i=l,k=l,j=mid+1;
while (i<=mid&&j<=r)
{
if (a[i]<=a[j]) b[k++]=a[i++];
else
{
tot+=mid-i+1;
b[k++]=a[j++];
}
}
while (i<=mid) b[k++]=a[i++];
while (j<=r) b[k++]=a[j++];
for (ll i=l;i<=r;i++) a[i]=b[i];
}

int main()
{
scanf("%d%d",&n,&m);
a[0]=0;
s[0]=0;
for (ll i=1;i<=n;i++)
{
scanf("%d",&a[i]);
s[i]=s[i-1]+a[i];
a[i]=m*i-s[i];
}
msort(0,n);

//for (ll i=0;i<=n;i++) prllf("%lld ",b[i]);
printf("%lld\n",tot);
return 0;
}

/*°ÂÉñ¹é²¢ÄæÐò¶ÔÄ£°å

ll a
, tmp
;
void msort(ll l, ll r)
{
ll mid = l + r >> 1;
if(l < r)
{
msort(l, mid);
msort(mid + 1, r);
}
ll i = l, j = mid+1, k = l;
while(i <= mid && j <= r)
{
if(a[i] <= a[j]) tmp[k++] = a[i++];
else
{
ans += mid - i + 1;
tmp[k++] = a[j++];
}
}
while(i <= mid) tmp[k++] = a[i++];
while(j <= r) tmp[k++] = a[j++];
for(i = l; i <= r; ++i) a[i] = tmp[i];
}
*/


——峡束苍江对起,过危楼,欲飞还敛。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息