• [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\%$的数据,$nleqslant 500,mleqslant 500$;
    对于$50\%$的数据,$nleqslant 30,000,mleqslant 30,000$;
    对于$100\%$的数据,$nleqslant 100,000,mleqslant 100,000$。


    题解

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

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

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

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

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

    时间复杂度:$Theta(nsqrt{n} imes 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
    ",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
    ",ans[i]);
    	return 0;
    }
    

    rp++

  • 相关阅读:
    其实php真的不错!!!
    mysql 中 时间和日期函数
    mysql grant 命令三种常用
    "设备用反线 不同设备用平行" 这条法则要好好理解.
    mysql 用户管理
    discuz! 页面含义及目录结构分析(转)
    Html TO Ubb and Ubb TO Html
    zend Development Environment 5.5 6.0 6.1 注册码
    discuz登陆相关资料
    linux中的定制任务 crontab
  • 原文地址:https://www.cnblogs.com/wzc521/p/11663632.html
Copyright © 2020-2023  润新知