D.出题人的手环-树状数组求逆序对数

题目链接:

https://ac.nowcoder.com/acm/contest/358/D

题目描述

出题人的妹子送了出题人一个手环,这个手环上有 n 个珠子,每个珠子上有一个数。

有一天,出题人和妹子分手了,想把这个手环从两个珠子间切开,并按顺时针顺序展开成一条链。

可以发现,这条链一共有 n 种可能性。求这 n 种可能性的逆序对数之积模 1000000007。

输入描述:

第一行一个数 n,表示珠子个数。
接下来一行 n 个数,以顺时针顺序给出每个珠子上的整数

输出描述:

一个数,表示答案。

输入

4
1 3 2 3

输出

24

说明

一共有 4 种方式:
1 3 2 3;3 1 3 2;2 3 1 3;3 2 3 1;
逆序对数分别为 1,3,2,4,积为 24。

备注:

n<=200000,-10^9<=珠子上的整数<=10^9。

题意:

出题人的妹子送了出题人一个手环,这个手环上有 n 个珠子,每个珠子上有一个数。

有一天,出题人和妹子分手了,想把这个手环从两个珠子间切开,并按顺时针顺序展开成一条链。

可以发现,这条链一共有 n 种可能性。求这 n 种可能性的逆序对数之积模 1000000007。

 思路:

离散化加树状数组,先求出第一种情况的逆序对,之后每次将最后一个数减去比他大的,加上比他小的就是下一个序列的逆序对数。

This is the code:

#include<algorithm>
#include<cstdio>
#include<cstring>
#include<cmath>
#include<cstdlib>
#include<iostream>
#include<iomanip>
#include<list>
#include<map>
#include<queue>
#include<sstream>
#include<stack>
#include<string>
#include<set>
#include<vector>
using namespace std;
#define PI acos(-1.0)
#define EPS 1e-8
#define MOD 1e9+7
#define LL long long
#define ULL unsigned long long     //1844674407370955161
#define INT_INF 0x7f7f7f7f      //2139062143
#define LL_INF 0x7f7f7f7f7f7f7f7f //9187201950435737471
const int dr[]= {0, 0, -1, 1, -1, -1, 1, 1};
const int dc[]= {-1, 1, 0, 0, -1, 1, -1, 1};
// ios::sync_with_stdio(false);
// 那么cin, 就不能跟C的 scanf,sscanf, getchar, fgets之类的一起使用了。
const int maxn=200005;
const int mod=1000000007;

struct node//储存输入的数据
{
    int v;
    int pos;
}in[maxn];

int aa[maxn];//离散化使用
int c[maxn];//树状数组使用
int n;

bool cmp(node a,node b)
{
    if(a.v==b.v)
        return a.pos<b.pos;
    return a.v<b.v;
}
int lowbit(int x)
{
    return x&-x;
}

int getSum(int x)
{
    int ret=0;
    while(x>0)
    {
        ret+=c[x];
        x-=lowbit(x);
    }
    return ret;
}

void add(int x, int d)
{
    while(x<=n)
    {
        c[x]+=d;
        x+=lowbit(x);
    }
}

int main()
{
    scanf("%d",&n);
    memset(aa,0,sizeof(aa));
    memset(c,0,sizeof(c));
    for(int i=1; i<=n; ++i)
    {
        scanf("%d",&in[i].v);
        in[i].pos=i;
    }
    //离散化
    sort(in+1,in+n+1,cmp);
    int pos=1;
    aa[in[1].pos]=1;
    for(int i=2; i<=n; ++i)
    {
        if(in[i].v==in[i-1].v)
            aa[in[i].pos]=pos;
        else
            aa[in[i].pos]=++pos;
    }
    //上面是离散化数据
    int ans=0;
    for(int i=1; i<=n; ++i)
    {
        add(aa[i],1);
        ans=ans+i-getSum(aa[i]);
        //getSum(aa[i])的意思是求树状数组中小于aa[i]的个数,
        //i-getSum(aa[i])就是求有多少大的,也就是逆序对数
        ans%=mod;
    }
    //上面树状数组求逆序对数的个数

    LL sum=ans;//注意需要这里要用long long 否则乘法会爆
    for(int i=1;i<n;++i)
    {
        ans=ans-getSum(aa[i]-1)+(n-getSum(aa[i]));
        //那么逆序对会减去比他小的数字数,再加上比他大的数字数。
        ans=(ans+mod)%mod;//注意这一步,ans可能位零
        sum=sum*ans%mod;
    }
    printf("%lld\n",sum);
    return 0;
}

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值