HDU2838 Cow Sorting 树状数组 区间求和加逆序数的应用

这题目意思很简单,就是给你一个数组,然后让你重新排好序,排序有要求的,每次只能交换两个元素的位置,交换需要一个代价 就是两个元素之和,问你把数组重小到大排好最少需要多少代价


可能一开始想不到逆序数,我是专门做专题往那边想才想到的,举个例子吧
数组: 9 1 0 5 4
此时到 0 的时候,我们先手写一下最小代价,然后再按照自己的猜测去看看,就是当前扫到0,那么前面比它大的数有2个,所以先 部分代价为 2 * 0,然后再加上前面比它大的数 也就是9 和1 ,那么最小代价为10,发现跟手算的一样,那么 再多试几个 最后我们就发现了


对于当前数num,前面有x个比它大的数,那么走到当前一步的 最小代价为 x*num 再加上前面比它大的数之和


这样就很容易跟树状数组扯上关系了,当前一步的逆序数 其实就是  前面比它大的数的个数,然后同时又能用树状数组对于前面比它大的数求和,这样问题就完美解决了,一开始我看n是10^5次,可能还是没经验把,觉得有可能会超,所以就先离散化的做了一遍,可是总是WA,然后离散化去掉以后就过了,不知道为什么,可是用过掉的代码跑了很多案例,发现跟离散化版本的 答案是一样的,真心不知道哪里写错了

离散化的贴出来,希望路过大神 指点:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ll long long
#define LL __int64
#define eps 1e-8

//const ll INF=9999999999999;

#define inf 0xfffffff

using namespace std;


//vector > G;
//typedef pair P;
//vector> ::iterator iter;
//
//mapmp;
//map::iterator p;

const int N = 500000  + 10;


int a[N];
int aa[N];
int n;

typedef struct Node {
	int v;//原数字
	int id;//下标
};

Node p[N];

typedef struct C {
	LL sum;
	int id;
};

C c[N];

void clear() {
    memset(c,0,sizeof(c));
	memset(aa,0,sizeof(aa));
	memset(p,0,sizeof(p));
}

bool cmp(Node x,Node y) {
	return x.v < y.v;
}

int lowbit(int x) {
    return x&(-x);
}
//设原始矩阵为a,将a[i]加上val时对c所做的修改
void update(int i, int val) {
    while (i <= n) {
		c[i].id += val;
        i += lowbit(i);
    }
} 

void add(int i,int val) {
	while(i <= n) {
		c[i].sum += (1LL) * val;
		i += lowbit(i);
	}
}

int get_sumid(int i) {
	int sum = 0;
	while(i > 0) {
		sum += c[i].id;
		i -= lowbit(i);
	}
	return sum;
}


//求前i项元素的和
int get_sum(int i) {
    int sum=0;
    while (i > 0) {
        sum += c[i].sum;
        i -= lowbit(i);
    }
    return sum;
}

int main() {
    while(scanf("%d",&n) == 1) {
		clear();
		//先离散操作
		for(int i=1;i<=n;i++) {
			scanf("%d",&p[i].v);
			a[i] = p[i].v;
			p[i].id = i;//循环序号必须从1开始
		}
		sort(p + 1,p + n + 1,cmp);
		for(int i=1;i<=n;i++)
			aa[p[i].id] = i;//aa数组存了原来大小信息
		LL ans = 0;
		for(int i=1;i<=n;i++) {
			update(aa[i],1);
			add(a[i],a[i]);
			int ans1 = i - get_sumid(aa[i]);//i代表当前已经插入的个数,ge_sum(aa[i])代表比aa[i]小的数个数,减去即为大的个数,即逆序数
			if(ans1 != 0) {
				LL ans2 = (1LL) * get_sum(n) - (1LL) * get_sum(a[i]);
				ans += (1LL) * ans1 * a[i] + ans2;
			}
		}
		printf("%I64d\n",ans);
	}
    return 0;
}

AC代码:

#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include

#define ll long long
#define LL __int64
#define eps 1e-8

//const ll INF=9999999999999;

#define inf 0xfffffff

using namespace std;


//vector > G;
//typedef pair P;
//vector> ::iterator iter;
//
//mapmp;
//map::iterator p;

const int N = 500000  + 10;

int n;

typedef struct C {
	LL sum;
	int id;
};

C c[N];

void clear() {
    memset(c,0,sizeof(c));
}

int lowbit(int x) {
    return x&(-x);
}
//设原始矩阵为a,将a[i]加上val时对c所做的修改
void update(int i, int val) {
	int j = i;
    while (i <= n) {
		c[i].id += val;
		c[i].sum += j;
        i += lowbit(i);
    }
} 

int get_sumid(int i) {
	int sum = 0;
	while(i > 0) {
		sum += c[i].id;
		i -= lowbit(i);
	}
	return sum;
}


//求前i项元素的和
LL get_sum(int i) {
    LL sum=0;
    while (i > 0) {
        sum += c[i].sum;
        i -= lowbit(i);
    }
    return sum;
}

int main() {
    while(scanf("%d",&n) == 1) {
		clear();
		LL ans = 0;
		for(int i=1;i<=n;i++) {
			int x;
			scanf("%d",&x);
			update(x,1);
			LL ans1 = i - get_sumid(x);//i代表当前已经插入的个数,ge_sum(aa[i])代表比aa[i]小的数个数,减去即为大的个数,即逆序数
			if(ans1 != 0) {
				LL ans2 = get_sum(n) - (1LL) * get_sum(x);
				ans += ans1 * x + ans2;
			}
		}
		printf("%I64d\n",ans);
	}
    return 0;
}



/*
4
1 3 2 4

5
1 5 3 2 4

5
5 4 3 2 1

7 
3 4 5 1 2 7 6

6
2 1 6 5 4 3

7
4 3 6 5 2 1 7

ans:
5
29
60
46
57
69
*/




你可能感兴趣的:(树状数组)