HDU-1394 逆序数求解 树状数组/线段树 求逆序数 +递推

The inversion number of a given number sequence a1, a2, ..., an is the number of pairs (ai, aj) that satisfy i < j and ai > aj. 

For a given sequence of numbers a1, a2, ..., an, if we move the first m >= 0 numbers to the end of the seqence, we will obtain another sequence. There are totally n such sequences as the following: 

a1, a2, ..., an-1, an (where m = 0 - the initial seqence) 
a2, a3, ..., an, a1 (where m = 1) 
a3, a4, ..., an, a1, a2 (where m = 2) 
... 
an, a1, a2, ..., an-1 (where m = n-1) 

You are asked to write a program to find the minimum inversion number out of the above sequences. 

Input

The input consists of a number of test cases. Each case consists of two lines: the first line contains a positive integer n (n <= 5000); the next line contains a permutation of the n integers from 0 to n-1. 

Output

For each case, output the minimum inversion number on a single line. 

Sample Input

10
1 3 6 9 0 8 5 7 4 2

Sample Output

16

题目大意:有n个数,求这n个数在每次当前第一个元素放到最后面得到的当前序列的逆序数,然后从这些逆序数中找到最小的那一个。

用树状数组暴力求解,这道题O(n^2)TLE所以只能想别的办法,找规律,递推一下,看来一个序列的逆序数在按题目的要求移动后是有规律的,也就是有公式的,那就看一下怎么递推,我先将样例的所有的逆序数打出

 22   29   32  29  20  29  22  21  16  17

上面就是样例的在移动后的所有的逆序数。(下面的数组都是从1开始的)

思路:先求出第一组逆序数,也就是原先的序列(后面递推要用到),得到22,现在将a[1]从数组中拿出这样答案就要减去a[1],y因为1后面比它小的只有0(对于这道题来说一个序列里是没有重复元素,否则这样递推就不对了),所以当前的答案为22-a[1],然后再将a[1]放到后面,这样就构成一个新的序列,答案为22-a[1]+n-a[1]-1;

为什么?因为你再将a[1]放到序列后面的时候,只要它比最大数n小,那就又能能构成n-a[i]-1个逆序数,这里需要减一是去掉本身。

递推关系:sum=sum-a[i]+n-a[i]-1;

#include <iostream>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<string>
#include<cmath>
#include<cstdio>
using namespace std;
const int M=200002;
const int INF=0x3f3f3f3f;
typedef long long ll;
#define lson        id<<1,l,m
#define rson        id<<1|1,m+1,r
int c[M<<2];
int lay[M<<2];

int n;
int lowbit(int x)
{
    return x&-x;
}
void add(int x)
{
    while(x<=n)
    {
        lay[x]+=1;x+=lowbit(x);
    }
}
ll sum(int x)
{
    ll res=0;
    while(x>0)
    {
        res+=lay[x];x-=lowbit(x);
    }
    return res;
}
int main(){
 
   int m;
   while(~scanf("%d",&n)){

    memset(c,0,sizeof c);
   for(int i=1;i<=n;i++)
    scanf("%d",&c[i]);
   ll ans=INF;
        ll cont=0;
        memset(lay,0,sizeof lay);
   for(int i=1;i<=n;i++)
   {
       add(c[i]+1);//这里要+1才能用树状数组,当为0的时候树状在数组是不能用的
       cont+=i-sum(c[i]+1);
   }


   for(int k=1;k<=n;k++)
    {
        cont=cont-c[k]+n-c[k]-1;
        ans=min(ans,cont);
    }
   printf("%lld\n",ans);
   }
   //cout<<endl;
    return 0;
}

下面是用线段树求得的

#include <iostream>
#include<cstring>
#include<algorithm>
#include<bitset>
#include<string>
#include<cmath>
#include<cstdio>
using namespace std;
const int M=200002;
const int INF=0x3f3f3f3f;
typedef long long ll;
#define lson        id<<1,l,m
#define rson        id<<1|1,m+1,r
int c[M<<2];
int lay[M<<2];
int a[M];
int n;
void push_up(int x)
{
    c[x]=c[x<<1]+c[x<<1|1];
}
void build(int id,int l,int r)
{
    if(l==r)
        {
            c[id]=0;return ;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
}
void detate(int id,int l,int r,int p)
{
    if(l==r)
    {
        c[id]++;return;//更该树的思想和树状数组差不多,不过线段树是有比自己大的就+1;
    }
    int m=(l+r)>>1;
    if(p<=m) detate(lson,p);
    else detate(rson,p);
    push_up(id);
}
int query(int id,int l,int r,int L,int R)
{
    if(L<=l&&R>=r)
        return c[id];
    int m=(l+r)>>1;int ans=0;
    if(L<=m) ans+=query(lson,L,R);
    if(R>m) ans+=query(rson,L,R);
        return ans;
}
int main(){
    //ios::sync_with_stdio(0);cin.tie(0);
   int m;
   while(~scanf("%d",&n)){
    memset(a,0,sizeof a);
    build(1,1,n);
   for(int i=1;i<=n;i++)
    scanf("%d",&a[i]);
   ll ans=INF;
        ll cont=0;
        memset(c,0,sizeof c);
       // memset(lay,0,sizeof lay);
   for(int i=1;i<=n;i++)
   {
       cont+=query(1,1,n,a[i]+1,n);//一定是更改树之前就要求比a[i]大的数是多少,然后再更该
       detate(1,1,n,a[i]+1);//树状数组是求比自己小的数是多少,但线段树只能求比自己大的数的
   }//数量,这是线段树的特点决定的,比如你输入7 6,如果你搜区间1--6,那比自己小的数是0;
   for(int i=1;i<=n;i++)//那cont+=i-0;此时i=2,矛盾了,所以只能求比自己大的数有几个
   {
      cont=cont-a[i]+n-a[i]-1;
    ans=min(ans,cont);
   }
   printf("%lld\n",ans);
   }
   //cout<<endl;

    return 0;
}

猜你喜欢

转载自blog.csdn.net/c___c18/article/details/81747738