spoj 861 SWAPS & uva 11990 "Dynamic'' Inversion(动态维护逆序对)

开发者福利!热门AI工具限时免费用 购周边即赠Coding Plan Lite,Claude Code、Cursor等20+工具畅享,效率翻倍! 阅读详情

题意:有150000个数,10000条操作,每次将第i个数变为x,讯问每次操作后逆序对的数目。


解法1:树状数组+treap,树状数组的每个元素都是一棵树,这样可以在lognlogn时间内查询前i项有多少个小于等于k的元素,当某个元素法师改变时,只需从树状数组中对应的treap中进行元素的删减,每次修改前后统计的第i个元素形成的逆序对数,便可知每次操作后逆序对的数目。由于Treap常熟很大,因此虽然复杂度不高但是效率很低

解法2:Sqrt(N)分块+排序,统计前i项小于k的元素个数,类似于spoj3261http://www.spoj.pl/problems/RACETIME/;其他操作类似于解法1,代码量少,常数小,效率较高。

#include <cstdio>
#include <algorithm>
#include<cmath>
using namespace std;
const int maxn = 250010;
const int maxm=510;
struct Block {
	int num[maxm], arr[maxm], len;
	bool dirty;
	void init(int n) {
		len = n;
		dirty = true;
	}
	int get(int k) {
		if (dirty) {
			for (int i = 1; i <= len; i++)
				arr[i] = num[i];
			sort(arr + 1, arr + len + 1);
			dirty = false;
		}
		int left = 1, right = len, ans = 0;
		while (left <= right) {
			int mid = (left + right) >> 1;
			if (arr[mid] <= k) {
				ans = mid;
				left = mid + 1;
			} else
				right = mid - 1;
		}
		return ans;
	}
	void update(int i, int v) {
		if (v == num[i])
			return;
		num[i] = v;
		dirty = true;
	}
	int query(int left, int right, int v) {
		int sum = 0;
		for (int i = left; i <= right; i++)
			if (num[i] <= v) {
				sum++;
			}
		return sum;
	}
} bk[maxm];
struct IndexTree {
	int ss[50020];
	const static int N = 50010;
	void init() {
		for (int i = 1; i <= N; i++)
			ss[i] = 0;
	}
	int lowbit(int k) {
		return (k & -k);
	}
	void inc(int i, int v) {
		while (i <= N) {
			ss[i] += v;
			i += lowbit(i);
		}
	}
	int get(int i) {
		int res = 0;
		while (i > 0) {
			res += ss[i];
			i -= lowbit(i);
		}
		return res;
	}
} all;
int belong[maxn], id[maxn], M, n,m,x,y;
int arr[maxn];
void build() {
	for (int i = 1; i * i <= n; i++)
		M = i;
	int cnt = 0, len = M;
	for (int i = 1; i <= n; i++) {
		if (len == M) {
			bk[++cnt].init(M);
			len = 0;
		}
		belong[i] = cnt;
		id[i] = ++len;
		bk[cnt].num[len] = arr[i];
	}
	bk[cnt].len = len;
}
int query(int k, int c) {
	int sum = 0;
	int b = belong[k];
	for (int i = 1; i < b; i++)
		sum += bk[i].get(c);
	sum += bk[b].query(1, id[k], c);
	return sum;
}

long long res;
void init() {
	res = 0;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		scanf("%d", arr + i);
		res += i - 1 - all.get(arr[i]);
		all.inc(arr[i], 1);
	}
}
int cal(int k) {// 计算第k项元素形成的逆数对个数
	int a = all.get(arr[k] - 1);
	int b = query(k, arr[k] - 1);
	int c = query(k, arr[k]);
	return a - b + k - c;
}
int main() {
	init();
	build();
	scanf("%d", &m);
	while (m--) {
		scanf("%d%d", &x, &y);
		res -= cal(x);
		all.inc(arr[x], -1);
		all.inc(y, 1);
		int cnt = belong[x];
		bk[cnt].update(id[x], y);
		arr[x] = y;
		res += cal(x);
		printf("%lld\n", res);
	}
	return 0;
}

Uva11990 "Dynamic'' Inversion(Rujia Liu's Present 3: A Data Structure Contest Celebrating the 100th Anniversary of Tsinghua University,Problem D)

这题M较大,因此使用第一种方法比第二种效率要高。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <cstdlib>
#include<cmath>
#include<map>
using namespace std;
const int maxn = 200010;
const int inf = 1 << 30;
struct TreeNode {
	int key, pri;
	int left, right, size, num;
	void init() {
		left = right = 0;
		num = size = 1;
		pri = rand();
	}
};
TreeNode nodes[maxn * 25];
int stack[maxn*25], top, cnt;
int newnode() {
	int ret;
	if (top)
		ret = stack[--top];
	else
		ret = ++cnt;
	nodes[ret].init();
	return ret;
}
struct Treap {
	int root;
	void init() {
		root = cnt = top = 0;
		nodes[0].pri = -0x7FFFFFFF;
	}
	void push_up(int idx) {
		nodes[idx].size = nodes[nodes[idx].left].size
				+ nodes[nodes[idx].right].size + nodes[idx].num;
	}
	void leftRotate(int &root) {
		int tmp = nodes[root].right;
		nodes[root].right = nodes[nodes[root].right].left;
		nodes[tmp].left = root;
		push_up(root);
		push_up(tmp);
		root = tmp;
	}
	void rightRotate(int &root) {
		int tmp = nodes[root].left;
		nodes[root].left = nodes[nodes[root].left].right;
		nodes[tmp].right = root;
		push_up(root);
		push_up(tmp);
		root = tmp;
	}
	void insert(int k) {
		insert(k, root);
	}
	void insert(int k, int& root) {
		if (nodes[root].key == k) {
			nodes[root].num++;
			nodes[root].size++;
			return;
		}
		if (!root) {
			root = newnode();
			nodes[root].key = k;
			return;
		}
		if (k < nodes[root].key) {
			insert(k, nodes[root].left);
			if (nodes[nodes[root].left].pri > nodes[root].pri)
				rightRotate(root);
		} else {
			insert(k, nodes[root].right);
			if (nodes[nodes[root].right].pri > nodes[root].pri)
				leftRotate(root);
		}
		push_up(root);
	}
	void del(int k) {
		del(root, k);
	}
	void del(int &root, int k) {
		if (nodes[root].key == k) {
			if (!nodes[root].left && !nodes[root].right) {
				if (nodes[root].num == 1) {
					stack[top++] = root;
					root = 0;
				} else {
					nodes[root].num--;
					nodes[root].size--;
				}
				return;
			}
			if (nodes[nodes[root].left].pri > nodes[nodes[root].right].pri) {
				rightRotate(root);
				del(nodes[root].right, k);
			} else {
				leftRotate(root);
				del(nodes[root].left, k);
			}
			push_up(root);
			return;
		}
		if (k < nodes[root].key)
			del(nodes[root].left, k);
		else
			del(nodes[root].right, k);
		push_up(root);
	}
	int find(int k) {
		return find(root, k);
	}
	int find(int root, int k) {
		if (root == 0)
			return 0;
		if (nodes[root].key == k)
			return nodes[nodes[root].left].size + nodes[root].num;
		if (nodes[root].key > k)
			return find(nodes[root].left, k);
		else
			return nodes[nodes[root].left].size + nodes[root].num + find(
					nodes[root].right, k);
	}
} tree;
int lowbit(int k) {
	return (k & -k);
}
struct node {
	int len;
	Treap tree;
	void init() {
		tree.init();
	}
	int get(int k) {
		return tree.find(k);
	}
	void update(int v) {
		if (v > 0)
			tree.insert(v);
		else
			tree.del(-v);
	}
};
struct IndexTree {
	int ss[maxn],N;
	void init(int n) {
		N=n;
		memset(ss, 0, sizeof(ss));
	}
	void inc(int i, int v) {
		while (i <= N) {
			ss[i] += v;
			i += lowbit(i);
		}
	}
	int get(int i) {
		int res = 0;
		while (i > 0) {
			res += ss[i];
			i -= lowbit(i);
		}
		return res;
	}
};
node ss[maxn];
int pos[maxn], arr[maxn], m, n, x;
IndexTree all, now;
long long res;
void init() {
	now.init(n);
	all.init(n);
	for (int i = 1; i <= n; i++)
		ss[i].init();
}
void update(int i, int v) {
	while (i <= n) {
		ss[i].update(v);
		i += lowbit(i);
	}
}
int query(int i, int v) {
	int res = 0;
	while (i > 0) {
		res += ss[i].get(v);
		i -= lowbit(i);
	}
	return res;
}
void build() {
	res = 0;
	for (int i = 1; i <= n; i++) {
		scanf("%d", &x);
		pos[x] = i;
		arr[i] = x;
		res += i - 1 - all.get(x);
		all.inc(x, 1);
		update(i, x);
		now.inc(i, 1);
	}
}
int cal(int k) {

	int a = all.get(arr[k] - 1);
	int b = query(k, arr[k] - 1);
	k = now.get(k);
	return a - b + k-b-1;
}
int main() {
	while (scanf("%d %d", &n, &m) != EOF) {
		init();
		build();
		while (m--) {
			printf("%lld\n",res);
			scanf("%d", &x);
			res -= cal(pos[x]);
			all.inc(x, -1);
			now.inc(pos[x], -1);
			update(pos[x], -x);
		}
	}
	return 0;
}





hdoj 5497 Inversion 【树状数组维护 区间逆序对 Inversion Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 360    Accepted Submission(s): 93 Problem Description You have a sequence 阅读详情

相关推荐

[BZOJ3295] [Cqoi2011]动态逆序对 (树套树)or(CDQ分治)

题意:N个数的排列,M次操作,每次求当前的逆序对数量并删掉一个数。 先说一下cdq分治做法。(5960kb,1.4s) 网上很多题解,我都看不懂(其实很多人的程序几乎是一样的,就改了一下变量名),然后就自己硬着头皮想了这道题,基本是独立做出来的,做出来之后竟然1A,简直愉快。不过我太辣鸡了想了半天才发现这本质上是一个三维偏序,分别是时间,下标,数值,记为(t,x,y)。 我们可以把删

AZUI _ oi memories 5281

树状数组求逆序对

树状数组求逆序对先回顾树状数组区间查询区间查询&amp;区间修改什么是逆序对,以及如何求求逆序对的方法如何用树状数组维护求逆序对逆序对题目 先回顾树状数组 树状数组是查询和修改都是logn的一种数据结构。 它里面的每个节点的值=自己加上所有子节点的值的和 有区间查询,单个节点的功能: 区间查询 求某段区间上所有数的和,是树状数组的强项。然鹅,他最常用的方法仅仅仅仅只是求前缀和 当然,也可以求区间和,让两个前缀相减即可 int lowbit(int x) { retrurn x&amp;-x; } int

qq_45895217的博客 1845

数据结构学习——动态逆序对

解法一: 分块套BIT利用树状数组算最初的逆序对个数 nlognnlogn 利用树状数组算出块中比x小的数的个数 lognlogn 遍历x所在的那个块 n/Sn/S 总复杂度O(m(n/S+Slogn)+nlognm(n/S+Slogn)+nlogn) S取nlogn−−−−−√\sqrt{nlogn}时 O(mnlogn−−−−−√+nlogn)m\sqrt{nlogn}+nlog

风雨兼程 856

动态统计逆序对 (树状数组 twopointer)

动态统计逆序对逆序对是一个非常经典的问题,对于一个序列Z,如果有 i < j 并且 Zi>Zj, 则我们称 (i,j)为一个逆序对。 但是逆序对虽然经典,却一个非常困难的问题因为列但是逆序对 。所以为简化问题,我们给定一个长度为N的序列Z和一个参数k, 我们希望知道有多少个 (L,R)满足 1≤L < R≤N,且Z1,Z2,⋯,ZL,ZR,⋯,ZN的逆序 对个数不超过k。【输入格式】 第一行两个

w4149 575

从普通逆序对到P3157动态逆序对

从普通逆序对逆序对进阶

zlq7777的博客 295

【Cqoi2011】【BZOJ3295】动态逆序对

Description对于序列A,它的逆序对数定义为满足iSolution树状数组一看到逆序对就要想到树状数组。 维护每个数前面到目前有多少个比自己大。动态的思路因为要动态维护,每次只删掉一个数在逆序对中的贡献:及每个数前面有多少个比自己大,每个数后面有多少数比自己小。 先预处理出每个数前面有多少个比自己大ll[i],每个数后面有多少个数比自己小rr[i]。 很显然每次只用总答案删掉位置为i的

Facico的博客 1185

动态逆序对,记校赛怨念

上周末的校赛决赛,A题是一道动态逆序对,这本是一道省选难度的模板题,P1393 动态逆序对 但因为数据过水,被我们暴力切了(在这里感谢sugar大佬)。 那么一道动态逆序对的正解究竟是什么呢?我们不能被人按在地上摩擦还最后还一无所知吧。经过一段时间的研究后,算是有所领悟,我决心做一点笔记。 什么是逆序对?? 对于给定的一段正整数序列,逆序对就是序列中 ai &gt; aj 且 i &lt; j...

fatdog_jo的博客 297

蒜头君的排序(树状数组维护逆序对

蒜头君是一个爱思考的好孩子,这一天他学习了冒泡排序,于是他就想,把一个乱序排列通过冒泡排序排至升序需要多少次交换,这当然难不倒他,于是他想来点刺激的,给定一个 1 \ldots n1…n 的排列,每次从该排列中选择一个区间 [l,r][l,r],问使用冒泡排序将该区间排至升序需要多少次交换操作。输入格式第一行一个整数 nn,表示排列长度。接下来一行 nn 个整数,表示该排列。接下来一行一个整数 m

starlords的博客 515

AcWing 楼蓝图腾(树状数组维护逆序对)

链接 题意: 给出你一段序列,让你找出yig9ong多少先增后减和多少先减后增的三元组。(i,j,k)(i&lt;j&lt;k)(i,j,k)(i&lt;j&lt;k)(i,j,k)(i&lt;j&lt;k) 分析: 用树状数组维护逆序对。先正序维护一个逆序对,再倒序维护逆序对,两两相乘再求和即可。 #include &lt;bits/stdc++.h&gt; using namespace std; #define ll long long const int maxn= 2e5+7; ll n;

林苏泽 227

BZOJ-3289-Mato的文件管理(莫队+树状数组维护逆序对个数)

题目 Sample Input 4 1 4 2 3 2 1 2 2 4 Sample Output 0 2 样例解释:第一天,Mato不需要交换 。第二天,Mato可以把2号交换2次移到最后。 /************************************************************** 3289: Mato的文件管理 Time Limit: 40 Sec ...

qq_42576687的博客 270

bestcoder#58 Inversion 即 hdu5497 (树状数组维护逆序数)

Inversion Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65536/65536 K (Java/Others) Total Submission(s): 752    Accepted Submission(s): 210 Problem Description You have a s

把复杂的事情简单描述 562

带修主席树(动态逆序对

树状数组套权值线段树 1.树状数组每个节点的值为该区间内权值线段树的根结点。 2.每棵权值线段树储存其根节点对应树状数组中所表示的区间内的值离散化后在线段树对应各区间内的的个数。 3.先边输入初始序列边求出初始逆序和,每次询问先输出一次逆序和,再删该数。 修改查询 如何求初始逆序和: 1.每次输入序列中一个数后,查询该数前面(包括该数)小于等于该数的数的个数,用当前数的位置(即当...

sedcftyv的博客 459

洛谷 P3157 [CQOI2011]动态逆序对(主席树+树状数组)

传送门 做这题前请先学动态第k小,这里blog。 首先我们有 对于序列A,它的逆序对数定义为满足i&amp;amp;amp;lt;j,且Ai&amp;amp;amp;gt;Aj的数对(i,j)的个数。 那么第一次输出时直接输出序列的逆序对个数,然后问题就转化为删去某个元素时删去的逆序对数。 若删去元素的位置为x,它产生的贡献就是原序列中区间[1,x-1]中大于x的数的个数与区间[x+1,n]中小于x的数的个数加上前面删去的...

Dawn_LLLLLLL 516

SPOJ-INVCNT】Inversion Count【树状数组】【逆序对

题意: 给出一列数,求逆序对个数。 裸题。 发现树状数组的n与数的个数n搞混了... 懒得离散化,反正时限大。 #include #include using namespace std; typedef unsigned long long ULL; const int maxn = 200005, maxm = 10000005; int n, a

BraketBN 979

动态逆序对

P1347 - [CQOI2011]动态逆序对 Description 对于序列A,它的逆序对数定义为满足i<j,且Ai>Aj的数对(i,j)的个数。给1到n的一个排列,按照某种顺序依次删除m个元素,你的任务是在每次删除一个元素之前统计整个序列的逆序对数。 Input 输入第一行包含两个整数n和m,即初始元素的个数和删除的元素个数。 ...

weixin_34364135的博客 255

hdu5775 Bubble Sort 树状数组维护逆序数

题目链接###题意解读 理解冒泡排序的过程 最左坐标值为当前坐标和最终坐标的最小值() 最右坐标值为当前坐标加右边较小数据的个数(每个比当前值小的右边的数据都会和当前值交换) 树状数组从右往左扫,将当前值对应在数组中的位置更新为1,求比其小的数已经有几个存在。 ps:描述不太严谨不想用数学公式咋办,还是看代码吧,理解就好。 #include<iostream> #include<cstdio> #i

ruojingzhang的博客 534

3295: [Cqoi2011]动态逆序对

白书有言:事实上,借助于分治法和Fenwick树,本题可以用更短的代码在更短的运行时间内解决。 当时一直不明白啊,于是就放弃了,反正树套树我也不会(摊手)。 今天突然想到,啊这不就是CDQ分治吗。 整体二分是二分答案,CDQ分治是二分操作。 于是就乱搞一下。 先算出初始序列的逆序对数,顺便算一下每个点的数所成的逆序对数,然后在操作的时候删除。 由于删除的时候会把同一个逆序对删两次,所以

月下沙茶树 422
上一篇: poj 2843 Cutting Cake
下一篇: hdu 4358 Boring counting
kksleric
博客等级 码龄16年 49粉丝 75原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值