bzoj4358 perm

4358: permu

Time Limit: 30 Sec   Memory Limit: 512 MB
Submit: 327   Solved: 98
[ Submit][ Status][ Discuss]

Description

给出一个长度为n的排列P(P1,P2,...Pn),以及m个询问。每次询问某个区间[l,r]中,最长的值域
连续段长度。

Input

第一行两个整数n,m。
接下来一行n个整数,描述P。
接下来m行,每行两个整数l,r,描述一组询问。

Output

对于每组询问,输出一行一个整数,描述答案。

Sample Input

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

Sample Output

3
3
4

HINT

对于询问[1,4],P2,P4,P1组成最长的值域连续段[1,3];

对于询问[5,8],P8,P5,P7组成最长的值域连续段[4,6];

对于询问[1,7],P5,P7,P3,P6组成最长的值域连续段[5,8]。

1<=n,m<=50000


Source




思路很棒!

每个点记录两个信息:f[i][j]表示i的子树中深度为j的节点的个数。g[i][j]表示满足d[a]-2*d[lca(a,b)]=d[b]-2*d[lca(a,b)]的二元点对(a,b)的个数。

从下向上启发式合并,每次将轻儿子的信息合并到重儿子上,总的时间复杂度为O(nlogn)。在合并的过程中同时计算出答案。




#include<iostream>
#include<cstdio>
#include<cstring>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<stack>
#define F(i,j,n) for(int i=j;i<=n;i++)
#define D(i,j,n) for(int i=j;i>=n;i--)
#define ll long long
#define pa pair<int,int>
#define maxn 50005
#define inf 1000000000
using namespace std;
int n,m,block,l,r,mx;
int a[maxn],pos[maxn],pl[maxn],pr[maxn],ans[maxn];
bool vst[maxn];
stack<pa> st;
struct data{int l,r,id;}b[maxn];
inline int read()
{
	int x=0,f=1;char ch=getchar();
	while (ch<'0'||ch>'9'){if (ch=='-') f=-1;ch=getchar();}
	while (ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}
inline bool cmp(data x,data y)
{
	return pos[x.l]==pos[y.l]?x.r<y.r:x.l<y.l;
}
inline bool cmp_id(data x,data y)
{
	return x.id<y.id;
}
inline void add(int x,int flg)
{
	vst[x]=true;
	int tl=x,tr=x;
	if (vst[x-1]) tl=pl[x-1];
	if (vst[x+1]) tr=pr[x+1];
	if (flg)
	{
		st.push(make_pair(tl,pr[tl]));
		st.push(make_pair(tr,pl[tr]));
	}
	pr[tl]=tr;pl[tr]=tl;
	mx=max(mx,tr-tl+1);
}
inline int query(int l,int r)
{
	memset(vst,false,sizeof(vst));
	memset(pl,0,sizeof(pl));
	memset(pr,0,sizeof(pr));
	mx=0;
	F(i,l,r) add(a[i],0);
	return mx;
}
int main()
{
	n=read();m=read();
	block=int(sqrt(n));
	F(i,1,n) a[i]=read();
	F(i,1,n) pos[i]=(i-1)/block+1;
	F(i,1,m)
	{
		b[i].l=read();b[i].r=read();b[i].id=i;
		if (pos[b[i].l]==pos[b[i].r])
		{
			ans[i]=query(b[i].l,b[i].r);
			b[i].l=0;
		}
	}
	sort(b+1,b+m+1,cmp);
	F(i,1,m) if (b[i].l)
	{
		if (pos[b[i].l]!=pos[b[i-1].l])
		{
			l=pos[b[i].l]*block;
			r=l-1;
			mx=0;
			memset(vst,false,sizeof(vst));
			memset(pl,0,sizeof(pl));
			memset(pr,0,sizeof(pr));
		}
		for(;r<b[i].r;r++) add(a[r+1],0);
		int tmp=mx;
		D(j,l-1,b[i].l) add(a[j],1);
		ans[b[i].id]=mx;
		mx=tmp;
		D(j,l-1,b[i].l) vst[a[j]]=false;
		while (!st.empty())
		{
			pl[st.top().first]=st.top().second;st.pop();
			pr[st.top().first]=st.top().second;st.pop();
		}
	}
	F(i,1,m) printf("%d\n",ans[i]);
	return 0;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值