问题: permu
时间限制: 30 Sec 内存限制: 512 MB
题面
题目描述
给出一个长度为n的排列P(P1,P2,...Pn),以及m个询问。每次询问某个区间[l,r]中,最长的值域
连续段长度。
输入格式
第一行两个整数n,m。
接下来一行n个整数,描述P。
接下来m行,每行两个整数l,r,描述一组询问。
输出格式
对于每组询问,输出一行一个整数,描述答案。
样例输入
8 3
3 1 7 2 5 8 6 4
1 4
5 8
1 7
样例输出
3
3
4
题解
线段树+莫队。
考虑维护区间连续值域长度最大值。
首先显然莫队。(毕竟是蒟蒻我在莫队专题里遇到的题目……)
序列操作、多次询问,最关键的是无修!(没学过带修莫队不行啊 /理直气壮)
于是考虑怎么在不同区间内转移。
我们先种一棵值域线段树维护点的存在与否。
我们假设li为某段区间从左边界开始的最长值域连续段,ri为从右边界开始的最长值域连续段,mi为区间中最长值域连续段,考虑如何转移。
当push_up的时候,li由左儿子的li转移而来。特殊地,我们考虑左儿子整个区间连续,则li由左儿子的区间大小加上右儿子的li转移而来。
同理,ri由右儿子的ri转移。特殊情况为右儿子的区间大小加上左儿子的ri。
考虑mi的转移。显然mi由左儿子的mi和右儿子的mi以及左儿子的ri与右儿子的li组成的新区间转移而来。
注意我们开的是值域线段树,因此可以保证区间的连续性。
于是线段树维护3个标记我们就可以方便的进行转移了。
然后就是莫队板子。不断在线段树中插入点,删除点并进行转移统计答案。
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define rint register int
using namespace std;
int n,m,a[50004],tk[50004],belong[50004],l,r,sum_q,ans[50004];
struct node1{int li,ri,mi,size;}t[800005];
struct node2{int l,r,id;}que[50004];
inline bool cmp(node2 a,node2 b)
{
return (belong[a.l]^belong[b.l])?belong[a.l]<belong[b.l]:((belong[a.l]&1)?a.r<b.r:a.r>b.r);
}
inline void build(rint k,rint l,rint r)
{
if(l==r){t[k].size=1;return ;}
int mid=(l+r)>>1;
build(k<<1,l,mid);
build(k<<1|1,mid+1,r);
t[k].size=t[k<<1].size+t[k<<1|1].size;
return ;
}
inline void update(rint k)
{
t[k].li=t[k<<1].li;t[k].ri=t[k<<1|1].ri;
if(t[k<<1].li==t[k<<1].size)t[k].li+=t[k<<1|1].li;
if(t[k<<1|1].ri==t[k<<1|1].size)t[k].ri+=t[k<<1].ri;
// cout<<"left:"<<t[k<<1].mi<<" right:"<<t[k<<1|1].mi<<" mid:"<<t[k<<1].ri+t[k<<1|1].li<<endl;
t[k].mi=max(max(t[k<<1].mi,t[k<<1|1].mi),t[k<<1].ri+t[k<<1|1].li);
}
inline void change(rint k,rint p,rint u,rint l,rint r)
{
// cout<<"change:"<<p<<endl;
if(l==u&&r==u)
{
// cout<<p<<endl;
t[k].mi=p;t[k].li=p;t[k].ri=p;
// cout<<"mi:"<<t[k].mi<<endl;
return ;
}
rint mid=(l+r)>>1;
if(u<=mid)change(k<<1,p,u,l,mid);
else change(k<<1|1,p,u,mid+1,r);
update(k);//cout<<t[k].mi<<endl;
return ;
}
int main()
{
scanf("%d %d",&n,&m);
sum_q=(int)sqrt(n);
for(rint i=1;i<=n;++i)
{
scanf("%d",&a[i]);
belong[i]=i/sum_q+1;
}
build(1,1,n);
for(rint i=1;i<=m;++i)
{
scanf("%d %d",&que[i].l,&que[i].r);
que[i].id=i;
}
sort(que+1,que+m+1,cmp);
for(rint i=que[1].l;i<=que[1].r;++i)
{
tk[a[i]]++;
if(tk[a[i]]==1)change(1,1,a[i],1,n);
}
ans[que[1].id]=t[1].mi;
l=que[1].l,r=que[1].r;
for(rint i=2;i<=m;++i)
{
// cout<<l<<" "<<que[i].l<<endl;
while(l<que[i].l){tk[a[l]]--;if(tk[a[l]]==0)change(1,0,a[l],1,n);l++;}
// cout<<l<<" "<<que[i].l<<endl;
while(l>que[i].l){l--;tk[a[l]]++;if(tk[a[l]]==1)change(1,1,a[l],1,n);}
while(r<que[i].r){r++;tk[a[r]]++;if(tk[a[r]]==1)change(1,1,a[r],1,n);}
while(r>que[i].r){tk[a[r]]--;if(tk[a[r]]==0)change(1,0,a[r],1,n);r--;}
ans[que[i].id]=t[1].mi;
}
for(rint i=1;i<=m;++i)
cout<<ans[i]<<endl;
return 0;
}
最开始没设初始区间调了半个小时……
ps:此题需要卡常,最好使用奇偶性排序法QAQ(18000(TLE63)->12000(AC))