【XSY2423】跳蚤(根号分治)

题面

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

题解

神奇的分类讨论。

首先注意到每次所有跳蚤都只会往右跳,也就是说只要某一只跳蚤跳出了 max ⁡ ( r i ) max(r_i) max(ri) 它就不会再有贡献了。(和 火神的鱼 类似)

R = max ⁡ ( r i ) R=max(r_i) R=max(ri)

考虑根号分治,将所有的跳蚤分成两类:将 t i > R t_i> sqrt{R} ti>R 的分为第一类,将 t i ≤ R t_ileq sqrt{R} tiR 的分为第二类。

  1. 对于第一类,我们暴力维护这些鱼的位置,因为他们最多跳 R sqrt{R} R 次就会跳出 R R R 了,也就是不会再有贡献了。

    维护的过程可以用树状数组,时间复杂度 O ( q R log ⁡ R ) O(qsqrt R log R) O(qR logR)

  2. 对于第二类,我们考虑对于每一种 t i t_i ti,维护一棵权值线段树,共 R sqrt R R 棵,那么对于每一种 t i t_i ti,根据题目的询问 [ l , r ] [l,r] [l,r],我能还原这些跳蚤一开始应该在那个区间 [ l ′ , r ′ ] [l',r'] [l,r],然后在这颗线段树内查询这个区间。

    R sqrt R R 棵线段树,每棵查询 O ( log ⁡ R ) O(log R) O(logR),总时间 O ( q R log ⁡ R ) O(qsqrt R log R) O(qR logR)

那么我们就能在 O ( q R log ⁡ R ) O(qsqrt R log R) O(qR logR) 的时间内维护所有操作了。

代码如下:

#include<bits/stdc++.h>

#define LN 17
#define SN 310
#define N 100010
#define lc(x) t[x].ch[0]
#define rc(x) t[x].ch[1]
#define lowbit(x) (x&-x)

using namespace std;

const int sn=300,n=100000;

struct data
{
	int x,t;
	data(){};
	data(int a,int b){x=a,t=b;}
}p[N];

struct Tree
{
	int ch[2],sum;
}t[N*LN];

int q,tot;
int node,root[SN];
int c[N];

void add(int x,int y)
{
	for(;x<=n;x+=lowbit(x)) c[x]+=y;
}

int getsum(int x)
{
	int ans=0;
	for(;x;x-=lowbit(x)) ans+=c[x];
	return ans;
}

void update(int &k,int l,int r,int x)
{
	if(!k) k=++node;
	t[k].sum++;
	if(l==r) return;
	int mid=(l+r)>>1;
	if(x<=mid) update(lc(k),l,mid,x);
	else update(rc(k),mid+1,r,x);
}

int query(int k,int l,int r,int ql,int qr)
{
	if(!k) return 0;
	if(ql<=l&&r<=qr) return t[k].sum;
	int mid=(l+r)>>1,ans=0;
	if(ql<=mid) ans+=query(lc(k),l,mid,ql,qr);
	if(qr>mid) ans+=query(rc(k),mid+1,r,ql,qr);
	return ans;
}

int main()
{
	scanf("%d",&q);
	int tim=0;
	while(q--)
	{
		int opt;
		scanf("%d",&opt);
		if(opt==1)
		{
			int x,t;
			scanf("%d%d",&x,&t);
			if(t>sn)
			{
				p[++tot]=data(x,t);
				add(x,1);
			}
			else update(root[t],-3e7,3e7,x-tim*t);//还原0时刻这只跳蚤应该在什么位置
		}
		if(opt==2)
		{
			tim++;
			int tmp=0;
			for(int i=1;i<=tot;i++)
			{
				add(p[i].x,-1);
				data now=p[i];
				now.x+=now.t;
				if(now.x<=n)
				{
					p[++tmp]=now;
					add(now.x,1);
				}
			}
			tot=tmp;
		}
		if(opt==3)
		{
			int l,r;
			scanf("%d%d",&l,&r);
			int ans=getsum(r)-getsum(l-1);
			for(int i=1;i<=sn;i++)
				ans+=query(root[i],-3e7,3e7,l-i*tim,r-i*tim);//还原[l,r]区间在0时刻应该在哪个位置
			printf("%d
",ans);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/ez-lcw/p/14448638.html