寒假训练1.16

昨晚重新打的莫队今天调试出来了
//这个是这一次的写法

//首先的话就是必须要找到的是这个区间内的贝壳的个数
//使用一个链表对于每一个的话都是存储他的上一个最近的地点
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
using namespace std;
const int N=5e4+40,M=2e5+40,K=1e6+40;
int col[N];
int n,m,len,ans[M],res;//记得是m的存在
int l,r;
int get(int x)
{
    return x/len;
}
struct node{
    int l,r,id;
    bool operator<(const node& a)const 
    {
        int x=get(l),y=get(a.l);
        if(x!=y)
        {
            return x<y;
        }
        return r<a.r;
    }
}q[M];
int num[K];
void del(int x,int k)
{
    num[k]--;
    if(!num[k]) res--;
}//这个是最近的
void add(int x,int k)
{
    if(!num[k]) res++;
    num[k]++;
}//这个是删除
void show()
{
    cout<<res<<" "<<l<<" "<<r<<endl;
}
int main(){
    scanf("%d",&n);
    for(int i=1;i<=n;i++)
    {
        scanf("%d",&col[i]);
        // col[i]++;
    }
    col[0]=1e6+30;//如果第一个的编号是0的话是会有问题的啊
    scanf("%d",&m);
    for(int i=0;i<m;i++)
    {
        scanf("%d%d",&q[i].l,&q[i].r);
        q[i].id=i;
    }
    len=sqrt(n);
    sort(q,q+m);
    int x,y;
    for(int i=0;i<m;i++){
        x=q[i].l,y=q[i].r;
        while(l>x){
            --l;
            add(l,col[l]);
        }          
        while(r<y){
            ++r;
            add(r,col[r]);
        }
        while(r>y&&l<r){
            del(r,col[r]);
            r--;
        }  
        while(l<x&&l<r){
            del(l,col[l]);
            l++;
        }
        ans[q[i].id]=res;
    }
    for(int i=0;i<m;i++)
    {
        printf("%d\n",ans[i]);
    }
    return 0;
}

带修改的莫队
最优复杂下的len的计算需要学习一下

#include<iostream>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=5e4+20,M=2e5+30,S=1e6+20;
int w[N],cnt[S],ans[M],len;
int get(int x)
{
    return x/len;
}
struct node{
    int l,r,id;
    bool operator<(node a)
    {
        if(get(l)!=get(a.l))
        {
            return get(l)<get(a.l);
        }
        return r<a.r;
    }
}q[M];
void add(int x,int& res)
{
    if(!cnt[w[x]])
    {
        res++;
    }
    cnt[w[x]]++;
}
void del(int x,int& res)
{
    cnt[w[x]]--;
    if(!cnt[w[x]])  res--;
}
int main(){
    int n;
    cin>>n;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i];//放在外面的时候可以节省空间
    }
    int m;
    cin>>m;
    len=sqrt((double) n);//区间的是除以m
    for(int i=1;i<=m;i++)
    {
        cin>>q[i].l>>q[i].r;
        q[i].id=i;
    }
    sort(q+1,q+m+1);
    int res=0;
    for(int k=1,i=0,j=1;k<=m;k++)
    {
        int id=q[k].id,l=q[k].l,r=q[k].r;
        while(i<r) 
        {
            i++;//对于这一个是有顺序的
            add(i,res);
        }
        while(i>r)
        {
            del(i,res);
            i--;
        }//减的时候是先减在加
        while(j>l)
        {
            j--;//对于左区间的话与右区间的不一样
            add(j,res);
        }
        while(j<l)
        {
            del(j,res);
            j++; 
        }//注意一下两边的改变
        ans[id]=res;
    }//注意边界问题
    for(int i=1;i<=m;i++)
        cout<<ans[i]<<endl;
    return 0;
}

回滚莫队
//调试的时候造了一组样例发现应该是初始化错了,但是一直找不到哪里忘记初始化了,最后才想起来每一次重新找新的块的起点的时候res需要进行清零而不是全局变量
//好像还有一个普通莫队的做法用的是记录出前驱和后继的在看看

#include<bits/stdc++.h>
using namespace std;
const int N=2e5+40;
int a[N],col[N];
int b[N],st[N],clear[N],ans[N],ma[N];
int cs;
vector<int>s;
int read() {
	int x=0,f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-') f=-1;c=getchar();}
	while(c>='0'&&c<='9') x=x*10+c-'0',c=getchar();
	return x*f;
} 

struct node{
	int l,r,i;
	bool operator<(const node& a)const {
		return (b[l]!=b[a.l])?(b[l]<b[a.l]):(r<a.r);
	}
}q[N];

int find(int x)
{
	return (lower_bound(s.begin(),s.end(),x)-s.begin())+1;	
}

int cal(int l,int r)
{
	int last[N],ans=0;
	for(int i=l;i<=r;i++)	last[col[i]]=0;
	for(int i=l;i<=r;i++){
		if(!last[col[i]]){
			last[col[i]]=i;
		}else {
			ans=max(ans,i-last[col[i]]);
		}
	}
	return ans;
}

int main()
{
	int n,m;
	n=read();
	for(int i=1;i<=n;i++)
	{
		a[i]=read();
		s.push_back(a[i]);
	}
	sort(s.begin(),s.end());
	s.erase(unique(s.begin(),s.end()),s.end());
	int len=sqrt(s.size());
	for(int i=1;i<=n;i++)
	{
		b[i]=(i-1)/len+1;//这个才是你们的分块 
	}
	for(int i=1;i<=n;i++)
	{
		col[i]=find(a[i]);
	}
	m=read();
	for(int i=0;i<m;i++)
	{
		q[i].l=read(),q[i].r=read();
		q[i].i=i;
	}
	sort(q,q+m);
	int bn=b[n];
	for(int i=0,j=1;j<=bn;j++)
	{
		//开始进行一个枚举
		int br=min(j*len,n),r=br,l,res=0;//重新计算啊 
		cs=0;//清楚标记
		for(;b[q[i].l]==j&&i<m;i++)
		{
			//然后的话开始枚举如果是同一个内部的话
			if(b[q[i].r]==j)
			{
				ans[q[i].i]=cal(q[i].l,q[i].r);
				continue;	
			} 
			l=br+1;
			//然后的话就是开始了新的做法了啊
			while(r<q[i].r){
				//开始进行添加啊
				r++;
				if(!st[col[r]]){
					ma[col[r]]=st[col[r]]=r;
					clear[cs++]=col[r];
					//还有一个的话是记录出最大的编号是多少就好了 
				}else {
					res=max(res,r-st[col[r]]);
					ma[col[r]]=max(ma[col[r]],r);
				}
			} 
			int tm=res;
			while(l>q[i].l)
			{
				--l;
				if(!st[col[l]]) {
					ma[col[l]]=l;
					st[col[l]]=l;
				}else {
					res=max(res,ma[col[l]]-l);
				}
			}
			ans[q[i].i]=res;
			while(l<=br)
			{
				if(ma[col[l]]==l)
				{
					st[col[l]]=ma[col[l]]=0;
				}//删除标志 
				l++;
			}
			res=tm;
			//然后的话就要开始进行一个删除了
		}
		for(int i=0;i<cs;i++)
		{
			ma[clear[i]]=st[clear[i]]=0;	
		} 
	}
	for(int i=0;i<m;i++)
	{
		printf("%d\n",ans[i]);
	}
	return 0;
}

背完50个6级词汇了

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值