hdu 1394(线段树)


这道题目是求逆序数的。要得到答案,需要利用一个结论,如果是0到n-1 的排列,那么如果第一个数放到最后,这与这个数列,逆序数减少a[i],增加n-a[i]-1;


隔了几天之后,再做这道题目,发现有点生疏了。

在题目中的数字是从 0--n-1的。因此在初始化的时候要 build( 0,n-1 ,1)

在query 函数里,是查询a[i]到n-1 一段区间里面的逆序数。


time 62ms
#include <iostream>
#include <stdio.h>
#include <algorithm>
using namespace std;
const int N=5005;
int sum[N<<2];
int a[N];
void push_up(int rt)
{  sum[rt]=sum[rt<<1]+sum[rt<<1|1] ; }
void build(int l,int r,int rt)
{
    sum[rt]=0;
    if(l==r) return ;
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
}
void updata(int l,int r,int rt,int p)
{
   if(l==r)
   {   sum[rt]++;
       return ;
   }
   int m=(l+r)>>1;
   if(p<=m)
      updata(l,m,rt<<1,p);
   else
      updata(m+1,r,rt<<1|1,p);
   push_up(rt);
}
int query(int l,int r,int rt,int s,int t)
{
   if(s<=l&& t>=r)
   {
       return sum[rt];
   }
   int m=(l+r)>>1;
   int ret=0;
   if(s<=m)
       ret+=query(l,m,rt<<1,s,t);
   if(t>m)
       ret+=query(m+1,r,rt<<1|1,s,t);
   return ret;
}
int main()
{
   int n;
   while(~scanf("%d",&n))
   {
        int ans=0;
        build(0,n-1,1);
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            ans+=query(0,n-1,1,a[i],n-1);
            updata( 0,n-1,1,a[i] );
        }
        int ret=ans;
        for(int i=0;i<n;i++)
        {
           ans=(ans-a[i] )+ (n-a[i]-1) ;
           ret=min(ret,ans );
        }
        printf("%d\n",ret);
    }
}

在使用zkw 线段树的做法的时候,时间减少到了46ms;

贴上代码



#include <iostream>
#include <stdio.h>
#include <algorithm>
#include <memory.h>
using namespace std;
const int N=5005;
int T[N<<2];
int M;
void push_up(int n)
{
    T[n]=T[n<<1]+T[n<<1|1];
}
void updata(int n)
{
    for(T[n+=M]+=1,n>>=1;n;n>>=1)
        push_up(n);
}
int query(int s,int t)
{
    int ans=0;
    for(s=s+M-1,t=t+M+1;s^t^1;s>>=1,t>>=1)
    {
        if(~s&1)  ans+=T[s^1];
        if( t&1)  ans+=T[t^1];
    }
    return ans;
}
int a[N];
int main()
{
    int n;
    while(scanf("%d",&n)!=EOF)
    {
        for(M=1;M<=n+1;M<<=1);
        int sum=0;
        memset(T,0,sizeof(T));
        for(int i=0;i<n;i++)
        {
            scanf("%d",&a[i]);
            a[i]++;  <span style="font-family: 'Courier New'; white-space: pre-wrap; color: rgb(0, 128, 0); line-height: 1.5 !important;">//</span><span style="font-family: 'Courier New'; white-space: pre-wrap; color: rgb(0, 128, 0); line-height: 1.5 !important;">注意后面add的时候不能在0位置上add</span>

            if(a[i]!=n)
                sum+=query(a[i]+1,n);
            updata(a[i]);
        }
        int ret=sum;
        for(int i=0;i<n;i++)
        {
            sum=sum+(n-a[i])-(a[i]-1);
            ret=min(ret,sum);
        }
        printf("%d\n",ret);
    }
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值