bzoj-3809 Gty的二逼妹子序列

141 篇文章 0 订阅
88 篇文章 0 订阅

题意:

给出一个长度为n的数列,每个数字在[1,n]内;

m次询问,查询[l,r]区间中值在[a,b]中的数字种类数;

n<=100000,m<=1000000;

内存限制为28M


题解:

出题人实在太丧病系列;

莫队算法+树状数组这个比较显然吧;

码了一发交上去MLE了,砍了砍内存的常数,还是MLE;

然后发现询问里不能记录左端点所在块。。。在cmp里现求是吗。。。

改完T了!加完读入优化还是T!

没办法,去找了找正解;

正解是把树状数组求和改成了分块求和;

看起来比较慢但是时间复杂度是降低的;

原来的树状数组每次插入logn查询logn,算法总复杂度O(n√nlogn);

而改成分块之后每次插入O(1)查询n,算法总复杂度O((n+m)n);

这题就这么卡过了,卡常数新技巧get√?

反正刷完这题我是第一次见到了bzoj的提交限制数;



代码:



#include<math.h>
#include<stdio.h>
#include<string.h>
#include<algorithm>
#define N 100001
using namespace std;
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while(ch>'9'||ch<'0'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
int n,bk;
struct node
{
	int l,r,a,b,no;
}Q[N*10];
int a[N],s[N],block[1000],ans[N*10];
bool vis[N];
inline int lowbit(int x)
{
	return x&(-x);
}
inline bool cmp(node a,node b)
{
	if(a.l/bk==b.l/bk)
	return a.r<b.r;
	return a.l/bk<b.l/bk;
}
inline void add(int x)
{
	vis[x]=!vis[x];
	block[x/bk]+=vis[x]?1:-1;
}
inline int query(int x,int y)
{
	int ret=0,bx=x/bk,by=y/bk;
	if(bx==by)
	{
		for(int i=x;i<=y;i++)
			ret+=vis[i];
		return ret;
	}
	for(int i=bx+1;i<by;i++)
		ret+=block[i];
	for(int i=x;i<(bx+1)*bk;i++)
		ret+=vis[i];
	for(int i=by*bk;i<=y;i++)
		ret+=vis[i];
	return ret;
}
inline void update(int x,int op)
{
	if(!s[x])	add(x);
	s[x]+=op;
	if(!s[x])	add(x);
}
int main()
{
	int m,i,j,k,l,r;
	scanf("%d%d",&n,&m);
	bk=sqrt(n);
	for(i=1;i<=n;i++)
		a[i]=read();
	for(i=1;i<=m;i++)
	{
		Q[i].l=read(),Q[i].r=read(),Q[i].a=read(),Q[i].b=read();
		Q[i].no=i;
	}
	sort(Q+1,Q+m+1,cmp);
	l=1,r=1,s[a[1]]=1,add(a[1]);
	for(i=1;i<=m;i++)
	{
		while(l<Q[i].l)	update(a[l++],-1);
		while(l>Q[i].l)	update(a[--l],1);
		while(r<Q[i].r)	update(a[++r],1);
		while(r>Q[i].r)	update(a[r--],-1);
		ans[Q[i].no]=query(Q[i].a,Q[i].b);
	}
	for(i=1;i<=m;i++)
		printf("%d\n",ans[i]);
	return 0;
}



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值