• 【BZOJ2141】排队 树状数组+分块


    【BZOJ2141】排队

    Description

    排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足i<j且hi>hj的(i,j)数量。幼儿园阿姨每次会选出两个小朋友,交换他们的位置,请你帮忙计算出每次交换后,序列的杂乱程度。为方便幼儿园阿姨统计,在未进行任何交换操作时,你也应该输出该序列的杂乱程度。

    Input

    第一行为一个正整数n,表示小朋友的数量;第二行包含n个由空格分隔的正整数h1,h2,…,hn,依次表示初始队列中小朋友的身高;第三行为一个正整数m,表示交换操作的次数;以下m行每行包含两个正整数ai和bi¬,表示交换位置ai与位置bi的小朋友。

    Output

    输出文件共m行,第i行一个正整数表示交换操作i结束后,序列的杂乱程度。

    Sample Input

    【样例输入】
    3
    130 150 140
    2
    2 3
    1 3

    Sample Output

    1
    0
    3
    【样例说明】
    未进行任何操作时,(2,3)满足条件;
    操作1结束后,序列为130 140 150,不存在满足i<j且hi>hj的(i,j)对;
    操作2结束后,序列为150 140 130,(1,2),(1,3),(2,3)共3对满足条件的(i,j)。
    【数据规模和约定】
    对于100%的数据,1≤m≤2*103,1≤n≤2*104,1≤hi≤109,ai≠bi,1≤ai,bi≤n。

    题解:我们先想朴素算法,对于操作(a,b)我们需要求出[a+1,b-1]内比a小、比a大、比b小、比b大的数的个数,这个很容易想到用树状数组,但是树状数组只能离线搞,因此我们想到分块

    我们对每个块都开一个树状数组,然后对于操作[i,j]我们先扫一遍中间的大块,用树状数组更新一下答案,然后再暴力枚举两边的小块,时间复杂度O(m*sqrt(n)*log(n))

    注意:

    1.一开始的初始化逆序对时最好新建一个普通的树状数组来搞,这样应该能大大减小复杂度

    2.可能出现两个身高相同的情况,这不应该被算作逆序对处理

    3.输入的a可能大于b

    4.别忘了特判(a,b)在同一个块里的情况

    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <cmath>
    #include <algorithm>
    using namespace std;
    int n,nm,m,siz,ans;
    int s[150][20010],v[20010],tr[20010];
    struct node
    {
    	int num,org;
    }p[20010];
    bool cmp(node a,node b)
    {
    	return a.num<b.num;
    }
    int rd()
    {
    	int ret=0;	char gc=getchar();
    	while(gc<'0'||gc>'9')	gc=getchar();
    	while(gc>='0'&&gc<='9')	ret=ret*10+gc-'0',gc=getchar();
    	return ret;
    }
    void updata(int x,int y,int z)
    {
    	for(int i=y;i<=nm;i+=i&-i)	s[x][i]+=z;
    }
    int query(int x,int y)
    {
    	int i,ret=0;
    	for(i=y;i;i-=i&-i)	ret+=s[x][i];
    	return ret;
    }
    int main()
    {
    	n=rd(),siz=int(sqrt(1.0*n));
    	int i,j,a,b,pre;
    	for(i=0;i<n;i++)	p[i].org=i,p[i].num=rd();
    	sort(p,p+n,cmp);
    	for(i=0,pre=-1;i<n;i++)
    	{
    		if(p[i].num>pre)	pre=p[i].num,nm++;
    		v[p[i].org]=nm;
    	}
    	for(i=n-1;i>=0;i--)
    	{
    		updata(i/siz,v[i],1);
    		for(a=0,j=v[i]-1;j;j-=j&-j)	a+=tr[j];
    		for(ans+=a,j=v[i];j<=nm;j+=j&-j)	tr[j]++;
    	}
    	printf("%d
    ",ans);
    	m=rd();
    	for(i=1;i<=m;i++)
    	{
    		a=rd()-1,b=rd()-1;
    		if(a>b)	swap(a,b);
    		if(a/siz==b/siz)
    		{
    			for(j=a+1;j<b;j++)	ans+=(v[a]<v[j])+(v[j]<v[b])-(v[a]>v[j])-(v[j]>v[b]);
    			ans+=(v[a]<v[b])-(v[a]>v[b]);
    			swap(v[a],v[b]);
    			printf("%d
    ",ans);
    			continue;
    		}
    		for(j=a/siz+1;j<b/siz;j++)	ans+=-query(j,v[a]-1)-query(j,v[a])+query(j,v[b]-1)+query(j,v[b]);
    		for(j=a+1;j<(a/siz+1)*siz;j++)	ans+=(v[a]<v[j])+(v[j]<v[b])-(v[a]>v[j])-(v[j]>v[b]);
    		for(j=b/siz*siz;j<b;j++)	ans+=(v[a]<v[j])+(v[j]<v[b])-(v[a]>v[j])-(v[j]>v[b]);
    		ans+=(v[a]<v[b])-(v[a]>v[b]);
    		updata(a/siz,v[a],-1),updata(b/siz,v[b],-1);
    		swap(v[a],v[b]);
    		updata(a/siz,v[a],1),updata(b/siz,v[b],1);
    		printf("%d
    ",ans);
    	}
    	return 0;
    }
  • 相关阅读:
    程序员面视题解析
    Forms身份验证
    梅花雨日历控件
    系统架构师学习笔记_第四章(下)
    系统架构师学习笔记_第三章
    系统架构师学习笔记_第六章(上)
    系统架构师学习笔记_第五章(下)
    系统架构师学习笔记_第一章
    系统架构师学习笔记_第四章(上)
    系统架构师学习笔记_第五章(上)
  • 原文地址:https://www.cnblogs.com/CQzhangyu/p/6568090.html
Copyright © 2020-2023  润新知