xjtuoj 1027: jwp的数学是跟谁学的

题目来源

不管什么题目先暴力交一遍就完事了

int a[1000005];
int main() {
	std::ios::sync_with_stdio(false);
	int n;
	n=read();
	ll ans=0;
#ifdef brute_force
	F(i,0,n) a[i]=read();
	F(i,0,n)
	F(j,i,n)
	if(a[i]>a[j])

sad小心暴力

总之是道金典逆序对题,可以用分治思想或者树状数组基础应用题

分治

主要学习了这篇博客里的方式。

回忆归并排序的实现方式,通过将一个序列划按相邻的数分为n个小段,转换为有序序列后逐步合并成大序列。因此只需要在排列的过程中加上计算逆序对的操作即可。

由于合并中途已经确保两个比较的区间有序,因此当\(a_i>a_{mid+k}\)时,意味着\(a_i\)\(a_{mid\ldots mid+k}\)都成逆序对

由于\(n_{max}=10^6\),注意答案要用\(long\,long\)(第一次交没改全局longlong第二次没改输出函数里的longlong,激情刷提交记录)

代码
ll marray(int s,int t,int mid,int a[]){
	ll i=s,j=mid+1;
	int w=0;
	ll ans=0;
	int *k=new int[t-s+1];
	while(i<=mid&&j<=t){
		if(a[i]<=a[j]){
			k[w++]=a[i++];
		}
		else{
			k[w++]=a[j++];
			ans+=mid-i+1ll;
		}
	}
	while(i<=mid){
		k[w++]=a[i++];
	}
	while(j<=t){
		k[w++]=a[j++];
	}
	for(int i=s,j=0;i<=t;i++,j++) a[i]=k[j];
	delete[] k;
	return ans;
}
ll msort(int s,int t,int a[]){
	if(s==t) return 0;
	int mid=(s+t)/2;
	ll ans=0;
	ans+=msort(s,mid,a);
	ans+=msort(mid+1,t,a);
	ans+=marray(s,t,mid,a);
	return ans;
}

树状数组

先开一个数组用于离散化原数组,然后再用树状数组维护下区间出现数字个数就好了(改下求前缀和部分的板子题)

代码
int a[M],d[M],tree[M],n;
ll an;
int lowbit(int x)
{
	return x&-x;
}
void add(int x)
{
	for(;x<=n;x+=lowbit(x)) tree[x]++;//改成当前区间包含有多少个数字
}
ll sum(int x)
{
	ll ans=0;
	for(;x;x-=lowbit(x)) ans+=tree[x];
	return ans;
}
bool cmpT(int x,int y){
	if(a[x]==a[y]) return x>y;
	return a[x]>a[y];
}
int main()
{
	std::ios::sync_with_stdio(false);
	n=read();
#ifdef Fenwick_Tree
	F(i,1,n+1){
		a[i]=read();
		d[i]=i;//Discretization
	}
	sort(d+1,d+n+1,cmpT);
	F(i,1,n+1){
		add(d[i]);
		an+=sum(d[i]-1);//计算比i小的有多少个
	}
	cout<<an;
#endif
	return 0;
}
posted @ 2022-04-10 17:48  FPICZEIT  阅读(8)  评论(0编辑  收藏  举报