hdu 5193 分块 树状数组 逆序对

题意:

给出n个数,a1,a2,a3,...,an,给出m个修改,每个修改往数组的某个位置后面插入一个数,或者把某个位置上的数移除。求每次修改后逆序对的个数。

限制:

1 <= n,m <= 20000; 1 <= ai <= n

思路:

插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个。

外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力。查找权值个数时,使用树状数组比较方便。内层通过树状数组维护权值。

每次更新即为,在前面块找比它大和在后面块中找比它小的代价。

O(m*(sqrt(n)+sqrt(n)*log(n)))

/*hdu 5193 分块 树状数组 逆序对
  题意:
  给出n个数,a1,a2,a3,...,an,给出m个修改,每个修改往数组的某个位置后面插入一个数,或者把某个位置上的数移除。求每次修改后逆序对的个数。
  限制:
  1 <= n,m <= 20000; 1 <= ai <= n
  思路:
  插入和删除用分块来处理,块与块之间用双向链表来维护,每一块用树状数组来求小于某个数的数有多少个。
  外层可以使用分块维护下标,这样添加和删除元素的时候,也很方便,直接暴力。查找权值个数时,使用树状数组比较方便。内层通过树状数组维护权值。
  每次更新即为,在前面块找比它大和在后面块中找比它小的代价。
  O(m*(sqrt(n)+sqrt(n)*log(n)))
 */
#include<iostream>
#include<cstdio>
#include<vector>
#include<cstring>
#include<cmath>
using namespace std;
#define LL __int64
#define PB push_back
const int N=355;
const int M=20005;
int tot;
vector<int> vec[N];
int pre[N],nxt[N];
int SZ;
int BIT[N][M];
int lowbit(int x){ return x&-x; }
int sum(int i,int x){
	int s=0;
	while(x>0){
		s+=BIT[i][x];
		x-=lowbit(x);
	}
	return s;
}
void update(int i,int x,int w){
	while(x<M){
		BIT[i][x]+=w;
		x+=lowbit(x);
	}
}
vector<int> A;
void init(){
	tot=0;
	memset(pre,-1,sizeof(pre));
	memset(nxt,-1,sizeof(nxt));
	memset(BIT,0,sizeof(BIT));
	for(int i=0;i<N;++i)
		vec[i].clear();
	A.clear();
}
LL merge_count(vector<int> &a){
	int n=a.size();
	if(n<=1) return 0;
	LL cnt=0;
	vector<int> b(a.begin(),a.begin()+n/2);
	vector<int> c(a.begin()+n/2,a.end());
	cnt+=merge_count(b);
	cnt+=merge_count(c);
	int ai=0,bi=0,ci=0;
	while(ai<n){
		if(bi<b.size() && (ci==c.size() || b[bi]<=c[ci]))
			a[ai++]=b[bi++];
		else{
			cnt+=(LL)(n/2-bi);
			a[ai++]=c[ci++];
		}
	}
	return cnt;
}
int count_left(int now,int h){
	int p=0;
	int ret=0;
	while(p!=now){
		ret+=vec[p].size()-sum(p,h);
		p=nxt[p];
	}
	return ret;
}
int count_right(int now,int h){
	int p=nxt[now];
	int ret=0;
	while(p!=-1){
		ret+=sum(p,h-1);
		p=nxt[p];
	}
	return ret;
}
void print(){
	cout<<"---"<<endl;
	int p=0;
	while(p!=-1){
			for(int j=0;j<vec[p].size();++j){
				cout<<vec[p][j]<<' ';
			}
			cout<<endl;
			p=nxt[p];
	}
	cout<<"---"<<endl;
}
int count_in(int now,int id,int h){
	int ret=0;
	for(int i=0;i<id;++i){	//
		if(vec[now][i]>h) ++ret;
	}
	for(int i=id;i<vec[now].size();++i){	//
		if(vec[now][i]<h) ++ret;
	}
	return ret;
}
int ins(int id,int h){
	int p=0;
	while(id>vec[p].size()){
		id-=vec[p].size();
		p=nxt[p];
	}
	vec[p].insert(vec[p].begin()+id,h);
	update(p,h,1);
	int left=count_left(p,h);
	int right=count_right(p,h);
	int in=count_in(p,id,h);
	if(vec[p].size()>SZ){
		int dy=vec[p][vec[p].size()-1];
		vec[p].pop_back();
		update(p,dy,-1);
		if(nxt[p]==-1 || vec[nxt[p]].size()==SZ){
			int q=nxt[p];
			nxt[p]=++tot;
			pre[tot]=p;
			nxt[tot]=q;
			pre[q]=tot;

			vec[tot].push_back(dy);
			update(tot,dy,1);
		}
		else{
			int q=nxt[p];
			vec[q].insert(vec[q].begin(),dy);
			update(q,dy,1);
		}
	}
	return left+in+right;
}
int qui(int id){
	int p=0;
	while(id>vec[p].size()){
		id-=vec[p].size();
		p=nxt[p];
	}
	int h=vec[p][id-1];
	int left=count_left(p,h);
	int right=count_right(p,h);
	int in=count_in(p,id,h);
	vec[p].erase(vec[p].begin()+id-1);	//
	update(p,h,-1);
	return left+right+in;
}
int main(){
	int n,m;
	int a;
	while(scanf("%d%d",&n,&m)!=EOF){
		init();
		SZ=sqrt(n+m);
		for(int i=0;i<n;++i){
			scanf("%d",&a);
			A.PB(a);
			vec[tot].PB(a);
			update(tot,a,1);
			if(vec[tot].size()==SZ){
				nxt[tot]=tot+1;
				++tot;
				pre[tot]=tot-1;
			}
		}

		LL ans=merge_count(A);
		int cm,x,y;
		for(int i=0;i<m;++i){
			scanf("%d",&cm);
			if(cm==0){
				scanf("%d%d",&x,&y);
				ans+=(LL)ins(x,y);
				printf("%I64d\n",ans);
			}
			else{
				scanf("%d",&x);
				ans-=(LL)qui(x);
				printf("%I64d\n",ans);
			}
			//print();
		}
	}
	return 0;
}

时间: 2024-08-06 21:58:35

hdu 5193 分块 树状数组 逆序对的相关文章

HDU 2689Sort it 树状数组 逆序对

Sort it Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total Submission(s): 4110    Accepted Submission(s): 2920 Problem Description You want to processe a sequence of n distinct integers by swapping two adjacent s

HDU - 2838 Cow Sorting (树状数组 + 逆序对)

HDU - 2838 Cow Sorting Time Limit: 1000MS   Memory Limit: 32768KB   64bit IO Format: %I64d & %I64u Submit Status Description Sherlock's N (1 ≤ N ≤ 100,000) cows are lined up to be milked in the evening. Each cow has a unique "grumpiness" lev

Bzoj 2789: [Poi2012]Letters 树状数组,逆序对

2789: [Poi2012]Letters Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 278  Solved: 185[Submit][Status][Discuss] Description 给出两个长度相同且由大写英文字母组成的字符串A.B,保证A和B中每种字母出现的次数相同. 现在每次可以交换A中相邻两个字符,求最少需要交换多少次可以使得A变成B. Input 第一行一个正整数n (2<=n<=1,000,000),表示字符串的长度

【树状数组逆序对】USACO.2011JAN-Above the median

[题意] 给出一串数字,问中位数大于等于X的连续子串有几个.(这里如果有偶数个数,定义为偏大的那一个而非中间取平均) [思路] 下面的数据规模也小于原题,所以要改成__int64才行.没找到测试数据,自己编的几组.简单来说读入每个数,大于等于中位数设为1,小于设为-1,前i个数的和建立一个树状数组,求逆序对. 1 #include<iostream> 2 #include<cstdio> 3 #include<cstring> 4 #include<algorit

Poj 2299 - Ultra-QuickSort 离散化,树状数组,逆序对

Ultra-QuickSort Time Limit: 7000MS   Memory Limit: 65536K Total Submissions: 52306   Accepted: 19194 Description In this problem, you have to analyze a particular sorting algorithm. The algorithm processes a sequence of n distinct integers by swappin

Codevs 3286 火柴排队 2013年NOIP全国联赛提高组 树状数组,逆序对

题目:http://codevs.cn/problem/3286/ 3286 火柴排队  2013年NOIP全国联赛提高组 时间限制: 1 s 空间限制: 128000 KB 题目等级 : 钻石 Diamond 题解 题目描述 Description 涵涵有两盒火柴,每盒装有 n 根火柴,每根火柴都有一个高度.现在将每盒中的火柴各自排成一列,同一列火柴的高度互不相同,两列火柴之间的距离定义为:,其中 ai表示第一列火柴中第 i 个火柴的高度,bi表示第二列火柴中第 i 个火柴的高度.每列火柴中相

总结之---树状数组+逆序对问题。

咳咳,这个图必须要的.... 首先,当有一个数组a数量非常大的时候,我们可能改变某个a[i]的值,要求a[n]的和,全部加起来,无疑是要O(n)的时间复杂度. 但是如果n非常大时,O(n)时间复杂度肯定要跪,所以,怎么办的,用神奇的树状数组. 树状数组代码简单,但是非常强大!更令人兴奋的是,它的时间复杂度值需要O(logn)!!! 好了,首先要的东西是把上图的c[n]表示出来,该怎么弄呢,代码如下: int lowbit(int t) { return t&(-t); } 这个代码,简单到爆,但

题解 SP4226 【MSE06H - Japan】(树状数组+逆序对)

原OJ提交点这里 这道题一开始让我很雾...... 不过 思路其实非常清晰:如果i<j a[i].x<a[j].x a[i].y>a[j].y 那么就会产生一个交点 大家画个图就出来了 具体操作也很好实现: 定义一个结构体 x升序排列 当x相同就y升序排列 按照我们的排序方式 把a[i].y踢出来跑树状数组求逆序对就可以了 附上AC代码 #include<cstdio> #include<iostream> #include<cmath> #inclu

Codeforces Round #609 (Div. 2)E--K Integers(贪心+二分+树状数组+逆序对)

K Integers 参考博客:https://blog.csdn.net/Q755100802/article/details/103664555 [题意] 给定一个1到n的排列,可以交换相邻的两个元素. 现在定义一个函数f(x),表示在原排列中,通过交换操作,形成一个1,2,3....x的排列的子串,需要的最小操作步骤. 子串意味着这个排列必须是相邻的.现在你需要求出f(1),f(2),f(3)......f(n). [分析] 在1~x这几个元素相邻的情况下,因为最后排列不存在逆序对,根据贪