BZOJ 3744 Gty的妹子序列 分块+树状数组+可持久化线段树

题目大意:给定一个序列,多次求区间内逆序对个数 强制在线

让我们呐喊一声:出题人卡常数丧心病狂!

再来一次:出题人卡常数丧心病狂!!!!

不强制在线的直接莫队就能搞 强制在线我是跪了QTZ

首先看这数据范围肯定O(n√nlogn)了 

我们分块 令cnt[i][j]为从第i块的开头起到第j个点这段区间的逆序对数

这个用树状数组就可以O(n√nlogn)搞出来 我一开始直接用可持久化线段树搞这部分 常数大TLE到死啊

然后对于每次查询,如果左右端点在同一块直接树状数组暴力,如果左右端点在不同一块的话 令temp为x所在块的下一块的左端点

对于[temp,y]部分的逆序对已经预处理出来了,直接调用即可

对于[x,temp-1]部分的逆序对,我们对于[x,temp-1]的每一个a[i]求出[i+1,y]区间内有多少比a[i]小的数,这个用可持久化线段树搞(总感觉不用 是我的错觉?)

然后就搞出来了……这TLE叫一个爽 上一个写法跑了整整30s我也是醉了 明明不强制在线还有40s的时限改成强制在线居然尼玛只剩15s了……

#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define M 50500
using namespace std;
struct abcd{
	abcd *ls,*rs;
	int num;
	void* operator new (size_t size,abcd *_,abcd *__,int ___);
}*tree[M],mempool[1001001],*C=mempool;
int n,m,tot,block,ans;
int a[M],l[M],r[M],belong[M];
int cnt[250][M];
pair<int,int>b[M];
int c[M],tim[M],T;
void Update(int x)
{
	for(;x;x-=x&-x)
	{
		if(tim[x]!=T)
			tim[x]=T,c[x]=0;
		c[x]++;
	}
}
int Get_Ans(int x)
{
	int re=0;
	for(;x<=tot;x+=x&-x)
		if(tim[x]==T)
			re+=c[x];
	return re;
}
void* abcd :: operator new (size_t size,abcd *_,abcd *__,int ___)
{
	C->ls=_;
	C->rs=__;
	C->num=___;
	return C++;
}
abcd* Build_Tree(abcd *p,int x,int y,int val)
{
	int mid=x+y>>1;
	if(x==y) return new (0x0,0x0,p->num+1)abcd;
	if(val<=mid) return new (Build_Tree(p->ls,x,mid,val),p->rs,p->num+1)abcd;
	else return new (p->ls,Build_Tree(p->rs,mid+1,y,val),p->num+1)abcd;
}
int Get_Ans(abcd *p1,abcd *p2,int x,int y,int l,int r)
{
	int mid=x+y>>1;
	if(p1->num==p2->num)
		return 0;
	if(x==l&&y==r)
		return p2->num-p1->num;
	if(r<=mid) return Get_Ans(p1->ls,p2->ls,x,mid,l,r);
	if(l>mid) return Get_Ans(p1->rs,p2->rs,mid+1,y,l,r);
	return Get_Ans(p1->ls,p2->ls,x,mid,l,mid) + Get_Ans(p1->rs,p2->rs,mid+1,y,mid+1,r);
}
inline int Query(int x,int y)
{
	int i,re=0;
	if(belong[x]==belong[y])
	{
		++T;
		for(i=x;i<=y;i++)
			re+=Get_Ans(a[i]+1),Update(a[i]);
		return re;
	}
	re+=cnt[belong[x]+1][y];	
	for(i=x;i<l[belong[x]+1];i++)
		re+=Get_Ans(tree[i],tree[y],0,tot+1,0,a[i]-1);
	return re;
}
int main()
{
	
	//freopen("3744.in","r",stdin);
	//freopen("3744.out","w",stdout);
	
	int i,j,k,x,y;
	cin>>n;
	for(i=1;i<=n;i++)
		scanf("%d",&b[i].first),b[i].second=i;
	sort(b+1,b+n+1);
	for(i=1;i<=n;i++)
	{
		if(i==1||b[i].first!=b[i-1].first)
			++tot;
		a[b[i].second]=tot;
	}
	tree[0]=new (0x0,0x0,0)abcd;
	tree[0]->ls=tree[0]->rs=tree[0];
	for(i=1;i<=n;i++)
		tree[i]=Build_Tree(tree[i-1],0,tot+1,a[i]);
	block=static_cast<int>(sqrt(n)+1e-7);
	for(i=1;i<=n;i++)
		belong[i]=(i-1)/block+1;
	for(i=1;(i-1)*block+1<=n;i++)
		l[i]=(i-1)*block+1,r[i]=min(i*block,n);
	for(i=1;(i-1)*block+1<=n;i++)
	{
		++T;
		for(j=(i-1)*block+1;j<=n;j++)
		{
			cnt[i][j]=cnt[i][j-1]+Get_Ans(a[j]+1);
			Update(a[j]);
		}
	}
	cin>>m;
	for(i=1;i<=m;i++)
	{
		scanf("%d%d",&x,&y);x^=ans;y^=ans;
		printf("%d\n", ans=Query(x,y) );
	}
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值