bzoj 3956: Count (单调栈+st表)

3956: Count

Time Limit: 10 Sec  Memory Limit: 512 MB
Submit: 177  Solved: 103
[ Submit][ Status][ Discuss]

Description

Input

Output

Sample Input

3 2 0
2 1 2
1 1
1 3

Sample Output

0
3

HINT

M,N<=3*10^5,Ai<=10^9


Source

[ Submit][ Status][ Discuss]

题解:单调栈+st表

观察序列发现,只有形如^就是山峰型的序列两侧才会产生合法点对,而低谷型的可以跨越最低点形成合法点对,点对一定不会跨越最高点,所以我们可以维护一个单调递减的单调栈,如果当前元素大于栈顶元素,就出栈形成合法的点对,主要出栈时相邻元素也要形成合法点对,对于高度相等的情况需要特殊的记录和处理,具体见代码。

然后我们对于序列中的每一个位置分别维护点对左右端点的前缀和。对于一个区间的询问,我们用st表查询出区间的最大值的位置,然后统计最大值左边左端点的数量和最大值右边右端点的数量,就是答案。为什么?因为如果是合法点对那么就不可能跨越最高点,所以左右分别统计就好了。

#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
#define N 1000003
using namespace std;
int n,m,h[N],st[N],suml[N],sumr[N];
int f[20][N],cnt,mark[N],l[N],type;
struct data
{
	int x,y;
}point[N*2];
int cmp1(data a,data b)
{
	return a.x<b.x;
}
int cmp2(data a,data b)
{
	return a.y>b.y;
}
void solve()
{
	for (int i=1;i<=n;i++)  f[0][i]=i;
	for (int i=1;i<=19;i++)
	 for (int j=1;j<=n;j++)
	  if (j+(1<<i)-1<=n)
	   {
	   	  int x=f[i-1][j];
	   	  int y=f[i-1][j+(1<<(i-1))];
	   	  if (h[x]<h[y])
	   	    f[i][j]=y;
	   	   else f[i][j]=x;
	   }
	int j=0;
	for (int i=1;i<=n;i++)
	 {
	 	if ((1<<(j+1))<=i)  j++;
	 	l[i]=j;
	 }
}
int calc(int x,int y)
{
	int k=l[y-x]; 
	int a=f[k][x];
	int b=f[k][y-(1<<k)+1];
	if (h[a]<h[b])  return b;
	else return a;
}
int main()
{
	scanf("%d%d%d",&n,&m,&type);
	for (int i=1;i<=n;i++)  scanf("%d",&h[i]);
	int top=1;
	st[top]=1;
	for (int i=2;i<=n;i++)
	 {
	 	if (h[i]<h[st[top]])
	 	 {
	 	 	++top;
	 	 	st[top]=i;
	 	 }
	 	else
	 	 {
	 	 	while(h[st[top]]<=h[i]&&top)
	 	 	 {
	 	 	 	++cnt;
	 	 	 	point[cnt].x=st[top]; point[cnt].y=i;
	 	 	 	if (h[st[top]]==h[i]&&top!=1) mark[i]=1;
	 	 	 	if (top-1&&!mark[st[top]])
	 	 	 	{
	 	 	 	++cnt;
	 	 	 	point[cnt].x=st[top-1]; point[cnt].y=st[top];
	 	 	    }
	 	 	    top--;
	 	 	 }
	 	 	st[++top]=i;
	 	 }
	 }
	 for (int i=top;i>=2;i--)
	  if (!mark[st[i]])
	  {
	  	++cnt;
	 	point[cnt].x=st[i-1]; point[cnt].y=st[i];
	  }
	 sort(point+1,point+cnt+1,cmp1);
	 for (int i=1;i<=cnt;i++)
	  suml[point[i].x]++,sumr[point[i].y]++;
	 for (int i=1;i<=n;i++) suml[i]+=suml[i-1];
	 for (int i=1;i<=n;i++) sumr[i]+=sumr[i-1];
     solve();
     int lastans=0;
     for (int i=1;i<=m;i++)
     {
     	int l,r; 
     	scanf("%d%d",&l,&r);
     	if (type)  l=(l+lastans-1)%n+1,r=(r+lastans-1)%n+1;
     	if (l>r) swap(l,r);
     	int t=calc(l,r); //cout<<t<<endl;
     	lastans=suml[t-1]-suml[l-1]+sumr[r]-sumr[t];
     	printf("%d\n",lastans);
     }
}



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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值