[CSP-S模拟测试]:ants(回滚莫队)

题目描述

  然而贪玩的$dirty$又开始了他的第三个游戏。
  $dirty$抓来了$n$只蚂蚁,并且赋予每只蚂蚁不同的编号,编号从$1$到$n$。最开始,它们按某个顺序排成一列。现在$dirty$想要进行$m$场比赛,每场比赛给出$l$和$r$,表示选出从左向右数第$l$只至第$r$只蚂蚁。被选出的蚂蚁需要快速地按编号从小到大排序,之后这些蚂蚁中编号连续的蚂蚁将围成一个圈。每场比赛结束后,蚂蚁们还需要快速地回到最开始的位置。
  按照蚂蚁的审美标准,围成的圈越大美观值就越大。于是$dirty$每次需要找到最大的圈,但由于比赛多到难以处理,他只需要知道这个圈中蚂蚁的数目。


输入格式

第一行为两个整数$n,m$,分别表示蚂蚁的总数和比赛场数。
接下来一行$n$个数,表示从左向右依次的蚂蚁编号。
再接下来$m$行,每行两个数$l$、$r$,表示将从左向右数第$l$只至第$r$只蚂蚁选出进行比赛。


输出格式

输出$m$行,每行一个整数表示该次询问的答案。


样例

样例输入:

8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7

样例输出:

3
3
4


数据范围与提示

对于$20\%$的数据,$n\leqslant 500,m\leqslant 500$;
对于$50\%$的数据,$n\leqslant 30,000,m\leqslant 30,000$;
对于$100\%$的数据,$n\leqslant 100,000,m\leqslant 100,000$。


题解

这道题其实就是$permu$的数据加强版。

超想先讲一下我考场上的思路。

首先想到了莫队,然后用线段树维护最长的连续编号,实现起来不难,时间复杂度:$\Theta(n\sqrt{n}\log n)$,然而极限数据跑了$7$秒多(听说我的还是快的……),但是这种做法可以卡过$permu$。

现在来讲正解,考虑换个思路维护,用并查集,因为颜色各不相同,合并的时候看一下左边和右边有没有,然后将其$size$相加即可,注意不要路径压缩就好了;但是删除操作复杂度较高,于是想到回滚莫队,然后这道题就没了……

不过正解是用链表代替的并查集(然而我不会……)

时间复杂度:$\Theta(n\sqrt{n}\times \omega)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

$50\%$算法:

#include<bits/stdc++.h>
#define L(x) x<<1
#define R(x) x<<1|1
using namespace std;
struct rec{int l,r,pos,id;}q[100001];
int n,m;
int a[100001];
int ans[100001];
int tr[500000],lc[500000],rc[500000];
bool cmp(rec a,rec b){return (a.pos)^(b.pos)?a.l<b.l:(((a.pos)&1)?a.r<b.r:a.r>b.r);}
void pushup(int x,int l,int r)
{
	tr[x]=max(tr[L(x)],tr[R(x)]);
	int mid=(l+r)>>1;
	if(lc[L(x)]==mid-l+1)lc[x]=lc[L(x)]+lc[R(x)];
	else lc[x]=lc[L(x)];
	if(rc[R(x)]==r-mid)rc[x]=rc[R(x)]+rc[L(x)];
	else rc[x]=rc[R(x)];
	if(rc[L(x)]&&lc[R(x)])tr[x]=max(tr[x],rc[L(x)]+lc[R(x)]);
}
void add(int x,int l,int r,int w)
{
	if(l==r)
	{
		tr[x]=lc[x]=rc[x]=1;
		return;
	}
	int mid=(l+r)>>1;
	if(w<=mid)add(L(x),l,mid,w);
	else add(R(x),mid+1,r,w);
	pushup(x,l,r);
}
void del(int x,int l,int r,int w)
{
	if(l==r)
	{
		tr[x]=lc[x]=rc[x]=0;
		return;
	}
	int mid=(l+r)>>1;
	if(w<=mid)del(L(x),l,mid,w);
	else del(R(x),mid+1,r,w);
	pushup(x,l,r);
}
int main()
{
	scanf("%d%d",&n,&m);
	int t=sqrt(n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].pos=(q[i].l-1)/t+1;
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	for(int i=q[1].l;i<=q[1].r;i++)
		add(1,1,n,a[i]);
	ans[q[1].id]=tr[1];
	int l=q[1].l,r=q[1].r;
	for(int i=2;i<=m;i++)
	{
		while(l>q[i].l)add(1,1,n,a[--l]);
		while(r<q[i].r)add(1,1,n,a[++r]);
		while(l<q[i].l)del(1,1,n,a[l++]);
		while(r>q[i].r)del(1,1,n,a[r--]);
		ans[q[i].id]=tr[1];
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}

$100\%$算法:

#include<bits/stdc++.h>
using namespace std;
struct rec{int l,r,pos,id;}q[100001];
int n,m;
int a[100001];
int ans[100001];
int sta[100001],size[100001],fa[100001],res,top;
bool vis[100001];
bool cmp(rec a,rec b){return a.pos==b.pos?a.r<b.r:a.pos<b.pos;}
int find(int x){return x==fa[x]?x:find(fa[x]);}
void merge(int x,int y)
{
	x=find(x);y=find(y);
	if(x==y)return;
	if(size[x]>size[y])x^=y^=x^=y;
	fa[x]=y;
	size[y]+=size[x];
	sta[++sta[0]]=x;
}
void add(int x)
{
	vis[x]=1;
	if(vis[x-1])merge(x,x-1);
	if(vis[x+1])merge(x,x+1);
	res=max(res,size[find(x)]);
}
void del()
{
	while(sta[0]>top)
	{
		size[find(sta[sta[0]])]-=size[sta[sta[0]]];
		fa[sta[sta[0]]]=sta[sta[0]];
		sta[0]--;
	}
}
int main()
{
	scanf("%d%d",&n,&m);
	int t=sqrt(n);
	for(int i=1;i<=n;i++)scanf("%d",&a[i]);
	for(int i=1;i<=m;i++)
	{
		scanf("%d%d",&q[i].l,&q[i].r);
		q[i].pos=(q[i].l-1)/t+1;
		q[i].id=i;
	}
	sort(q+1,q+m+1,cmp);
	int l,r,pos;
	for(int i=1;i<=m;i++)
	{
		if(q[i].pos!=q[i-1].pos)
		{
			for(int j=1;j<=n;j++)
			{
				fa[j]=j;
				vis[j]=0;
				size[j]=1;
			}
			res=sta[0]=0;
			l=pos=q[i].pos*t+1;
			r=l-1;
		}
		if(q[i].pos==(q[i].r-1)/t+1)
		{
			int now=1,maxn=1;
			for(int j=q[i].l;j<=q[i].r;j++)sta[++sta[0]]=a[j];
			sort(sta+1,sta+sta[0]+1);
			for(int j=1;j<=sta[0];j++)
			{
				if(sta[j]==sta[j-1]+1&&j>1)now++;
				else now=1;
				maxn=max(maxn,now);
			}
			ans[q[i].id]=maxn;
			sta[0]=0;
		}
		else
		{
			while(r<q[i].r)add(a[++r]);
			int flag=res;
			top=sta[0];
			while(l>q[i].l)add(a[--l]);
			ans[q[i].id]=res;
			res=flag;
			while(l<pos)vis[a[l++]]=0;
			del();
		}
	}
	for(int i=1;i<=m;i++)printf("%d\n",ans[i]);
	return 0;
}


rp++

原文地址:https://www.cnblogs.com/wzc521/p/11663632.html

时间: 2024-10-09 15:44:09

[CSP-S模拟测试]:ants(回滚莫队)的相关文章

Codeforces 620F Xors on Segments 回滚莫队 + 字典树 || 离心询问分治 + 可持久化字典树

Xors on Segments 转换一下变成询问区间选两个数异或的最大值, 要注意的是一个数作为左端点要-1, 所以在回滚莫队的时候用两棵字典树维护. 这个题居然n ^ 2 也能过...  其实用分治 + 可持久化字典树可以做到n * log(n) * log(n), 懒得写了... #include<bits/stdc++.h> #define LL long long #define LD long double #define ull unsigned long long #defin

AT1219 歴史の研究(回滚莫队)

题目描述 IOI国历史研究的第一人——JOI教授,最近获得了一份被认为是古代IOI国的住民写下的日记.JOI教授为了通过这份日记来研究古代IOI国的生活,开始着手调查日记中记载的事件. 日记中记录了连续N天发生的时间,大约每天发生一件. 事件有种类之分.第i天(1<=i<=N)发生的事件的种类用一个整数Xi?表示,Xi?越大,事件的规模就越大. JOI教授决定用如下的方法分析这些日记: 选择日记中连续的一些天作为分析的时间段 事件种类t的重要度为t*(这段时间内重要度为t的事件数) 计算出所有

习题:历史研究(回滚莫队)

题目 传送门 思路 很版的一道回滚莫队的题 我们如果用普通的莫队,我们发现最难维护的是最大值, 因为你无法预测缩减时最大值的变化,还要带一个线段树或者什么来维护 时间复杂度为\(O(n*log_n*\sqrt n)\) 但是我们想,我们如果已知一个莫队的左端点和右端点以及它的最大值 那么这个莫队向外拓展我们是很容易维护的 之后如果下一个操作也是向外拓展就向外拓展,如果是内缩, 我们就将这个莫队还原成为我们最开始已知的样子,在进行拓展 这也就是回滚莫队的主要思想,这道题也是如此 时间复杂度依然也是

[CSP-S模拟测试]:sum(数学+莫队)

题目传送门(内部题63) 输入格式 第一行有一个整数$id$,表示测试点编号.第一行有一个整数$q$,表示询问组数.然后有$q$行,每行有两个整数$n_i,m_i$. 输出格式 一共有$q$行,每行一个整数表示每组询问的答案$S_{n_i,m_i}$对$10^9+7$取模的结果. 样例 样例输入: 151 12 13 24 35 5 样例输出: 2371532 数据范围与提示 对于所有数据,$1\leqslant q,n_i,m_i\leqslant 10^5$. 题解 考场上把$80$分部分分

[CSP-S模拟测试]:飘雪圣域(莫队)

题目描述 $IcePrincess\text{_}1968$和$IcePrince\text{_}1968$长大了,他们开始协助国王$IceKing\text{_}1968$管理国内事物. $IcePrincess\text{_}1968$和$IcePrince\text{_}1968$住在一个宁静悠远的王国:$IceKingdom$——飘雪圣域.飘雪圣域有$n$个城镇,编号$1,2,3...n$.有些城镇之间有道路,且满足任意两点之间有且仅有一条路径.飘雪圣域风景优美,但气候并不是太好.根据$

CSP-S模拟测试69 题解

一如既往的垃圾,又回到了那个场场垫底的自己,明明考场上都想到正解了,但是就是拿不到分,可能是互奶把rp用光了吧以后一定加强训练代码能力. T1: 考场上一直yy矩阵快速幂,虽然自己矩阵快速幂一点都不会还是硬着头皮yy,发现不可做之后并没有及时转化思路,但其实自己预处理的数组就是正解. 切记:不仅矩阵快速幂是log的,普通快速幂也是2333 然后这题其实很水啊,我们设$dp[i][j]$为前$i$列放$j$个棋子的方案数,然后枚举最后一列放多少个棋子就好了. 转移方程为$dp[i][j]=\sum

csp-s模拟测试69

A. chess 看到范围一开始猜是矩阵快速幂,开始推按列转移的dp,正方形之间有交集不好转移. 换了个角度,都恰好有c个,那么我在滑动正方形的时候损失几个就要获得几个,然后就看出了列之间的相等关系. 推了个$\Theta(n^4logm)$的dp.码完测了下n=50都T飞,怀疑人生10min,发现k没限制n打成了$\Theta(n^5logm)$ 改完发现100依然T飞,然后慌了(因为懊悔T3是原题但我没学回滚莫队...),忘记当时为什么以为log提不出来了orz. 打完dp,观察复杂度瓶颈,

loj#6285 数列分块入门 9 ( 回 滚 )

题目 :  链接 :https://loj.ac/problem/6285 题意:给出一个长为 n的数列,以及 n个操作,操作涉及询问区间的最小众数. 思路:虽然这不是一道 回滚莫队题,就是 暴力分块 的题, 但是 还是 可以用回滚莫队 写滴,好像大部分题解都是 暴力分块. #include<bits/stdc++.h> #define LL long long #define ULL unsigned long long #define rep(i,j,k) for(int i=j;i<

csps模拟测试50反思

又考崩了,T1一眼秒掉错误思路,然后迅速码完,并码完错误暴力,对拍拍上,以为AC.T2想到了二维莫队,发现是子任务就没去打,一直在想别的,T3最后想到60分打法,没有打完,也没时间暴力,挂掉.T2还有一个读错题的锅,T了一个子任务. 考试一定要合理分配时间,确定自己算法的正确性,想到一个类似的算法要敢于去实现. T1 施工 单调栈优化dp 改变dp定义是优化dp的重要方式 dp[i]表示第i个位置不变的最优答案.枚举j转移,O(n^2),期望得分53 考虑优化,dp[i]只会由最多一个h比它大的