【bzoj3744】Gty的妹子序列 分块+树状数组+主席树

题目描述

我早已习惯你不在身边,

人间四月天 寂寞断了弦。

回望身后蓝天,

跟再见说再见……

某天,蒟蒻Autumn发现了从 Gty的妹子树(bzoj3720) 上掉落下来了许多妹子,他发现

她们排成了一个序列,每个妹子有一个美丽度。

Bakser神犇与他打算研究一下这个妹子序列,于是Bakser神犇问道:"你知道区间

[l,r]中妹子们美丽度的逆序对数吗?"

蒟蒻Autumn只会离线乱搞啊……但是Bakser神犇说道:"强制在线。"

请你帮助一下Autumn吧。

给定一个正整数序列a,对于每次询问,输出al...ar中的逆序对数,强制在线。

输入

第一行包括一个整数n(1<=n<=50000),表示数列a中的元素数。

第二行包括n个整数a1...an(ai>0,保证ai在int内)。

接下来一行包括一个整数m(1<=m<=50000),表示询问的个数。

接下来m行,每行包括2个整数l、r(1<=l<=r<=n),表示询问al...ar中的逆序对数(若ai>aj且i<j,则为一个逆序对)。

l,r要分别异或上一次询问的答案(lastans),最开始时lastans=0。保证涉及的所有数在int内。

输出

对每个询问,单独输出一行,表示al...ar中的逆序对数。

样例输入

4
1 4 2 3
1
2 4

样例输出

2



题解

分块+树状数组+主席树

由于题目强制在线,所以不能离线乱搞了。

正常来说,在线查询区间内比某数大/小的数的个数,使用的数据结构是主席树。

然而这样依然要查询询问区间内每个元素,这样时间复杂度还是不能下降。

我们想到可以使用分块预处理,查询时只查询块外元素,能够使时间复杂度降低。

具体地,设f[i][j]表示从第i块开始,到第j个位置结束的逆序对数。这样枚举每个i,就能够在$O(n\log n)$的时间内预处理。

对于每个查询,找到查询区间内第一个整块,根据f数组得到它到区间右端的逆序对数,这样剩下的就只有区间左端块外元素,使用主席树查询即可。

总时间复杂度为$O((n+m)\sqrt n\log n)$,另外听大爷说本题卡常,所以在预处理时需要使用树状数组。

#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define N 100010
using namespace std;
int a[N] , v[N] , sum[250][N] , f[N] , n , ls[N << 4] , rs[N << 4] , si[N << 4] , root[N] , tot;
void update(int x)
{
	int i;
	for(i = x ; i <= n ; i += i & -i) f[i] ++ ;
}
int query(int x)
{
	int i , ans = 0;
	for(i = x; i ; i -= i & -i) ans += f[i];
	return ans;
}
void insert(int p , int l , int r , int x , int &y)
{
	y = ++tot , si[y] = si[x] + 1;
	if(l == r) return;
	int mid = (l + r) >> 1;
	if(p <= mid) rs[y] = rs[x] , insert(p , l , mid , ls[x] , ls[y]);
	else ls[y] = ls[x] , insert(p , mid + 1 , r , rs[x] , rs[y]);
}
int calc(int p , int l , int r , int x , int y)
{
	if(l > p) return 0;
	if(r <= p) return si[y] - si[x];
	int mid = (l + r) >> 1;
	return calc(p , l , mid , ls[x] , ls[y]) + calc(p , mid + 1 , r , rs[x] , rs[y]);
}
int main()
{
	int m , i , j , si , last = 0 , x , y , ans;
	scanf("%d" , &n) , si = (int)sqrt(n);
	for(i = 0 ; i < n ; i ++ ) scanf("%d" , &a[i]) , v[i] = a[i];
	sort(v , v + n);
	for(i = 0 ; i < n ; i ++ ) a[i] = lower_bound(v , v + n , a[i]) - v , insert(a[i] , 0 , n - 1 , root[i] , root[i + 1]);
	for(i = 0 ; i <= n / si ; i ++ )
	{
		memset(f , 0 , sizeof(f)) , update(n - a[i * si]);
		for(j = i * si + 1 ; j < n ; j ++ ) sum[i][j] = sum[i][j - 1] + query(n - a[j] - 1) , update(n - a[j]);
	}
	scanf("%d" , &m);
	while(m -- )
	{
		scanf("%d%d" , &x , &y) , x = (x ^ last) - 1 , y = (y ^ last) - 1 , ans = 0;
		if(x / si == y / si)
			for(i = y - 1 ; i >= x ; i -- )
				ans += calc(a[i] - 1 , 0 , n - 1 , root[i + 1] , root[y + 1]);
		else
		{
			ans += sum[x / si + 1][y];
			for(i = (x / si + 1) * si - 1 ; i >= x ; i -- )
				ans += calc(a[i] - 1 , 0 , n - 1 , root[i + 1] , root[y + 1]);
		}
		printf("%d\n" , last = ans);
	}
	return 0;
}
时间: 2024-12-11 13:16:45

【bzoj3744】Gty的妹子序列 分块+树状数组+主席树的相关文章

【bzoj1146】[CTSC2008]网络管理Network 倍增LCA+dfs序+树状数组+主席树

题目描述 M公司是一个非常庞大的跨国公司,在许多国家都设有它的下属分支机构或部门.为了让分布在世界各地的N个部门之间协同工作,公司搭建了一个连接整个公司的通信网络.该网络的结构由N个路由器和N-1条高速光缆组成.每个部门都有一个专属的路由器,部门局域网内的所有机器都联向这个路由器,然后再通过这个通信子网与其他部门进行通信联络.该网络结构保证网络中的任意两个路由器之间都存在一条直接或间接路径以进行通信. 高速光缆的数据传输速度非常快,以至于利用光缆传输的延迟时间可以忽略.但是由于路由器老化,在这些

【BZOJ】1146: [CTSC2008]网络管理Network(树链剖分+线段树套平衡树+二分 / dfs序+树状数组+主席树)

第一种做法(时间太感人): 这题我真的逗了,调了一下午,疯狂造数据,始终找不到错. 后来发现自己sb了,更新那里没有打id,直接套上u了.我.... 调了一下午啊!一下午的时光啊!本来说好中午A掉去学习第二种做法,噗 好吧,现在第一种做法是hld+seg+bst+二分,常数巨大,log^4级别,目前只会这种. 树剖后仍然用线段树维护dfs序区间,然后在每个区间建一颗平衡树,我用treap,(这题找最大啊,,,囧,并且要注意,这里的rank是比他大的数量,so,我们在二分时判断要判断一个范围,即要

hdu_5788_Level Up(树状数组+主席树)

题目链接:hdu_5788_Level Up 题意: 有一棵树,n个节点,每个节点有个能力值A[i],mid[i],mid的值为第i节点的子树的中位数(包括本身),现在让你将其中的一个节点的A值改为1e5,问所有的mid的和最大问多少. 题解: 我们可以知道,如果改变其中一个的A[i],如果A[i]是比他父亲节点的mid小,那么他父亲的此时的中位数就会向后移一位 比如 1 2 3 4 5,第3个点是第2个点的父亲,如果改变了第二个点的A值,那么此时变成了1 3 4 5 1e5,第二个点的父亲的m

zoj2112--Dynamic Rankings(树状数组+主席树)

题目连接:zoj2112 给出n个点,两种操作,Q:询问在[l,r]内的第k大的数,C:更改第i个数位x 动态的询问第k大,使用树状数组修改和查询前缀和. 因为给出的空间小,所以可以将n个点做成一个静态的主席树,然后对于修改的值,在另一个主席树中修改,查询时同时查询这两个主席树就可以了. #include <cstdio> #include <cstring> #include <algorithm> using namespace std ; #pragma comm

待修改主席树 (树状数组+主席树)

终于学了这个我仰慕已久的算法. 对于待修改的主席树我们只需要多开一维,进行修改后的求和.复杂度进化为O(nlog^2n) 我们需要开R0 L0两个数组记录树状数组的"路径" 然后其他操作就和主席树一样咯! 1 #include<bits/stdc++.h> 2 using namespace std; 3 const int N=60006; 4 vector<int>v; 5 int n,m,cnt,mx; 6 int R0[N],L0[N],a[N],R[N

ZOJ 2112 Dynamic Rankings(树状数组+主席树)

The Company Dynamic Rankings has developed a new kind of computer that is no longer satisfied with the query like to simply find the k-th smallest number of the given N numbers. They have developed a more powerful system such that for N numbers a[1],

HDU 1394 Minimum Inversion Number 树状数组&amp;&amp;线段树

题目给了你一串序列,然后每次 把最后一个数提到最前面来,直到原来的第一个数到了最后一个,每次操作都会产生一个新的序列,这个序列具有一个逆序数的值,问最小的你逆序数的值为多少 逆序数么 最好想到的是树状数组,敲了一把很快,注意把握把最后一个数提上来对逆序数的影响即可, #include<iostream> #include<cstdio> #include<list> #include<algorithm> #include<cstring> #i

hdu 1166 树状数组 线段树

敌兵布阵 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submission(s): 51177    Accepted Submission(s): 21427 Problem Description C国的死对头A国这段时间正在进行军事演习,所以C国间谍头子Derek和他手下Tidy又开始忙乎了.A国在海岸线沿直线布置了N个工兵营地,Derek和Tidy的任务

【BZOJ】1047: [HAOI2007]理想的正方形(单调队列/~二维rmq+树状数组套树状数组)

http://www.lydsy.com/JudgeOnline/problem.php?id=1047 树状数组套树状数组真心没用QAQ....首先它不能修改..而不修改的可以用单调队列做掉,而且更快,只有O(n^2).而这货是n^2log^2n的建树...虽然查询是log^2n...但是建树那里就tle了.. 那么说题解... 先orz下,好神.. 我怎么没想到单调队列orz 首先我们维护 行 的单调队列,更新每个点在 列 距离内的最小and最大的值 然后我们维护 列 的单调队列,更新每个点