bzoj2141: 排队(树套树 或 分块)

2141: 排队

Time Limit: 4 Sec   Memory Limit: 259 MB
Submit: 1696   Solved: 696
[ Submit][ Status][ Discuss]

Description

排排坐,吃果果,生果甜嗦嗦,大家笑呵呵。你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家乐和和。红星幼儿园的小朋友们排起了长长地队伍,准备吃果果。不过因为小朋友们的身高有所区别,排成的队伍高低错乱,极不美观。设第i个小朋友的身高为hi,我们定义一个序列的杂乱程度为:满足ihj的(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,不存在满足ihj的(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。

HINT

Source


学分块的时候遇到这道题,当时想到了树套树的做法,课后就用树套树水过了,后来发现分块练习题一万个不会写,于是又回头来用分块写了这道题。

夯实基础很重要啊QAQ,不要总想搞个***


思路:

不难得知,杂乱程度即为逆序对个数,那么每次交换(x,y)后逆序对个数的变化只与[x,y]这段区间有关,则问题转化成查询区间内权值在[a,b]的个数。

①树套树可以解决这种问题: 

例题:bzoj3110: [Zjoi2013]K大数查询

②分块,对于每个块开一个树状数组。


树套树代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<climits>
#include<queue>
#include<stack>
#include<map>
#define N 200002
#define M 5000002
using namespace std;
int n,m,q[N],ans;
int hash[N],tot,num[M];
int sum[M],root[N],ls[M],rs[M],sz;
void insert(int &k,int l,int r,int w,int f)
{
	if(!k)k=++sz;sum[k]+=f;
	if(l==r)return;int mid=(l+r)>>1;
	if(w<=mid)insert(ls[k],l,mid,w,f);
	else insert(rs[k],mid+1,r,w,f);
}
int query(int &k,int l,int r,int a,int b)
{
	if(!k)return 0;int mid=(l+r)>>1;
	if(a<=l&&b>=r)return sum[k];
	if(b<=mid)return query(ls[k],l,mid,a,b);
	else if(a>mid)return query(rs[k],mid+1,r,a,b);
	else return query(ls[k],l,mid,a,mid)+query(rs[k],mid+1,r,mid+1,b);
}
void ins(int pos,int w,int f)
{
	int l=1,r=tot,k=1;
	while(l!=r)
	{
		int mid=(l+r)>>1;
		insert(root[k],1,n,pos,f);
		if(w<=mid)k=k<<1,r=mid;
		else k=k<<1|1,l=mid+1;
	}insert(root[k],1,n,pos,f);
}
int ask_bit(int a,int b,int w)
{
	int l=1,r=tot,k=1,ret=0;
	while(l!=r)
	{
		int mid=(l+r)>>1;
		if(w<=r&&w>mid)
		{
			ret+=query(root[k<<1],1,n,a,b);
			k=k<<1|1,l=mid+1;
		}
		else
			k=k<<1,r=mid;
	}return ret;
} 
int ask_big(int a,int b,int w)
{
	int l=1,r=tot,k=1,ret=0;
	while(l!=r)
	{
		int mid=(l+r)>>1;
		if(w>=l&&w<=mid)
		{
			ret+=query(root[k<<1|1],1,n,a,b);
			k=k<<1,r=mid;
		}
		else k=k<<1|1,l=mid+1;
	}return ret;
}
/*
3

13 15 14

2

2 3

1 3
*/
int x[N],y[N],cnt;
void init()
{
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&q[i]);
		num[++cnt]=q[i];
	}scanf("%d",&m);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&x[i],&y[i]);
		num[++cnt]=x[i];
		num[++cnt]=y[i];
	}sort(num+1,num+1+cnt);
	num[0]=-(1<<30);
	for(int i=1;i<=cnt;i++)
		if(num[i]!=num[i-1])
			hash[++tot]=num[i];
}
int find(int x)
{
	int l=1,r=tot,ret;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(hash[mid]<=x)
			l=mid+1,ret=mid;
		else r=mid-1;
	}return ret;
}
int main()
{
	freopen("in.txt","r",stdin);
	freopen("std.txt","w",stdout);
	scanf("%d",&n);init();
	for(int i=1;i<=n;i++)
	{
		int ii=find(q[i]);
		ans+=ask_big(1,i,ii);
		ins(i,ii,1);
	}printf("%d\n",ans);
	for(int i=1;i<=m;i++)
	{
		int a=x[i],b=y[i];
		if(a>b)swap(a,b);
		int ia=find(q[a]),ib=find(q[b]);
		ans-=ask_big(a+1,b,ib);
		ans+=ask_bit(a+1,b,ib);
		ans-=ask_bit(a,b-1,ia);
		ans+=ask_big(a,b-1,ia);
		if(ia>ib)ans--;
		else if(ia<ib)ans++;
		ins(a,ia,-1);ins(b,ib,-1);
		ins(a,ib,1);ins(b,ia,1);
		swap(q[a],q[b]);
		printf("%d\n",ans);
	}
}


分块代码:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<string>
#include<climits>
#include<queue>
#include<stack>
#include<map>
#include<set>
#define N 20010
using namespace std;
int read()
{
	int x=0,f=1;char ch;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int hash[N],tot,num[N];
int n,m,ans,B,l[N],r[N];
int q[N],C[200][N],bel[N];
int find(int x)
{
	int l=1,r=tot,ret;
	while(l<=r)
	{
		int mid=(l+r)>>1;
		if(hash[mid]>=x)
			r=mid-1,ret=mid;
		else l=mid+1;
	}return ret;
}
int lowbit(int x){return x&(-x);}
void add(int id,int x,int val)
{while(x<=tot){C[id][x]+=val;x+=lowbit(x);}}
int ask(int id,int x)
{int ret=0;while(x){ret+=C[id][x];x-=lowbit(x);}return ret;}
void solve(int x,int y)
{
	int L=min((x-1)/B+2,(n-1)/B+1),R=(y-1)/B;
	int s=find(q[x]),t=find(q[y]);
	for(int i=L;i<=R;i++)
	{
		ans+=ask(i,tot)-ask(i,s);
		ans-=ask(i,s-1);
		ans+=ask(i,t-1);
		ans-=ask(i,tot)-ask(i,t);
	}int l1=x+1,r1=l[L],l2=r[R]+1,r2=y;
	if(L>R)l1=x+1,r1=y;
	for(int i=l1;i<r1;i++)
	{
		if(q[i]>q[x])ans++;
		else if(q[i]<q[x])ans--;
		if(q[i]>q[y])ans--;
		else if(q[i]<q[y])ans++;
	}if(L>R)l2=r2+1;
	for(int i=l2;i<r2;i++)
	{
		if(q[i]>q[x])ans++;
		else if(q[i]<q[x])ans--;
		if(q[i]>q[y])ans--;
		else if(q[i]<q[y])ans++;
	}
	if(q[x]>q[y])ans--;else if(q[x]<q[y])ans++;
	int tx=find(q[x]),ty=find(q[y]);
	add(bel[x],tx,-1);add(bel[x],ty,1);
	add(bel[y],ty,-1);add(bel[y],tx,1);
	swap(q[x],q[y]);
}
int main()
{
//	freopen("in.txt","r",stdin);
//	freopen("my.txt","w",stdout);
	n=read();B=sqrt(n);int i;
	for(i=1;i<=n;i++)
	{
		q[i]=read();
		num[i]=q[i];
	}sort(num+1,num+1+n);
	for(i=1;i<=n;i++)
		if(num[i]!=num[i-1])
			hash[++tot]=num[i];
	for(i=1;i<=n;i++)
	{
		bel[i]=(i-1)/B+1;int t=find(q[i]);
		ans+=ask(0,tot)-ask(0,t);add(0,t,1);
	}printf("%d\n",ans);
	for(i=1;i<=(n-1)/B+1;i++)l[i]=0x3f3f3f3f;
	for(i=1;i<=n;i++)
	{
		r[bel[i]]=max(i,r[bel[i]]);
		l[bel[i]]=min(l[bel[i]],i);
		add(bel[i],find(q[i]),1);
	}m=read();
	for(i=1;i<=m;i++)
	{
		int x=read(),y=read();
		if(x>y)swap(x,y);
		solve(x,y);printf("%d\n",ans);
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值