您的位置:首页 > 其它

HDU 1394 树状数组||线段树

2013-10-14 19:27 363 查看
给出一个序列,from 0 to n-1,如果2个数满足下标小的值大,那么逆序数的和+1

可以把最左边的数 放到最右边,按此规则变换,求过程中产生的最小的逆序数的和

思路

先求出原始序列的逆序数的和,然后如果每次移动最左边的数到最右边,那么逆序数的变化是可以预知的

因为由于最左边的数产生的逆序数 为他右边比他小的数个数,而除了他自己,剩下的数都在他右边,一共n-1个,比他小的数的个数就是str[i];

而最左边的数放到最右边所新产生的逆序数也是可以预知的,为(把最左边放到最右边之后)他左边比他大的数,一共n-1个,比他大的个数是n-1-str[i];

求原始序列的时候,只需要统计一下在这个数之前出现过的比他小的数的个数x,那么他后面比他小的个数即str[i]-x;

树状数组 和线段树都可以很方便的统计出x.

需要注意的就是 数字的值从0到n-1;

线段树版...算是第一个纯手写的>_<了

单点更新+区间查询

#include<stdio.h>
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
#include<string.h>
#include<algorithm>
using namespace std;
int sum[5005<<2];
void pushUP(int rt){
sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}
int query(int L,int R,int l,int r,int rt){
if(L<=l&&r<=R){
return sum[rt];
}
int mid=(l+r)>>1;
int sum=0;
if(L<=mid)sum+=query(L,R,lson);
if(mid<R)sum+=query(L,R,rson);
return sum;
}
void update(int x,int l,int r,int rt){
if(l==r){
sum[rt]++;
return ;
}
int mid=(l+r)>>1;
if(x<=mid)update(x,lson);
if(x>mid)update(x,rson);
pushUP(rt);
}
int str[5005];
int main(){

int n,tp,ans;
while(scanf("%d",&n)!=EOF){
tp=ans=0;
memset(sum,0,sizeof(sum));
for(int i=0;i<n;i++){
scanf("%d",&str[i]);
tp+=str[i]-query(0,str[i],0,n-1,1);
update(str[i],0,n-1,1);
}ans=tp;
for(int i=0;i<n;i++){
tp=tp-str[i]+(n-1-str[i]);
ans=min(tp,ans);
}printf("%d\n",ans);

}return 0;
}


树状数组 要注意 str[i]值要+1,因为无法处理=0的状况

#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
int c[5005],n;
int lowbit(int x){
return -x&x;
}
int get(int x){
int ans=0;
while(x>0){
ans+=c[x],x-=lowbit(x);
}return ans;
}
void add(int x,int d){
while(x<=n){
c[x]+=d,x+=lowbit(x);
}
}
int main(){
int str[5005],tp;
while(scanf("%d",&n)!=EOF){
tp=0;
memset(c,0,sizeof(c));
for(int i=0;i<n;i++){
scanf("%d",&str[i]);
str[i]++;
tp+=i-get(str[i]);
add(str[i],1);
}int ans=tp;
for(int i=0;i<n;i++){
tp=tp-str[i]+1+(n-str[i]);
ans=min(tp,ans);
}
printf("%d\n",ans);
}return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: