【bzoj3956】【Count】【主席树+单调栈】

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

题解:可以发现点对数不会大于2*n,且点对之间不跨立.
           所以我们可以先用单调栈求出所有的点对,主席树维护一下即可.
           注意处理数值相等的情况.
代码:
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300010
using namespace std;
int a[N],ls[N*42],rs[N*42],sum[N*42],n,m,tp,cnt,num,top,l,r,ans,root[N];
struct use{int val,id;}q[N];
struct point{int l,r;}p[N*2];
bool cmp(point a,point b){return a.l==b.l?a.r<b.r:a.l<b.l;}
int read(){
  int x=0;char ch=getchar();
  while (ch<'0'||ch>'9') ch=getchar();
  while (ch>='0'&&ch<='9') x=x*10+ch-'0',ch=getchar();
  return x;
}
void insert(int x,int &y,int l,int r,int p){
   y=++cnt;
   ls[y]=ls[x];rs[y]=rs[x];
   if (l==r) {sum[y]=sum[x]+1;return;}
   int mid=(l+r)>>1;
   if (p<=mid) insert(ls[x],ls[y],l,mid,p);
   else insert(rs[x],rs[y],mid+1,r,p);
   sum[y]=sum[ls[y]]+sum[rs[y]];
} 
int query(int x,int y,int l,int r,int ll,int rr){
   int mid=(l+r)>>1;
   if (ll==l&&r==rr) return sum[y]-sum[x];
   if (rr<=mid) return query(ls[x],ls[y],l,mid,ll,rr);
   else if (mid<ll) return query(rs[x],rs[y],mid+1,r,ll,rr);
   else return query(ls[x],ls[y],l,mid,ll,mid)+query(rs[x],rs[y],mid+1,r,mid+1,rr); 
}
int main(){
  n=read();m=read();tp=read();
  for (int i=1;i<=n;i++) a[i]=read(); 
  top=0;num=0;
  for (int i=1;i<=n;i++){
    int flag(0);
    while (top&&a[i]>=q[top].val){p[++num]=point{q[top].id,i};if (a[i]==q[top].val) flag=1;top--;}
    if (top&&!flag) p[++num]=point{q[top].id,i};
    q[++top]=use{a[i],i};
  }     
  sort(p+1,p+num+1,cmp);top=1;
  for (int i=1;i<=n;i++){
    root[i]=root[i-1];
    while (top<=num&&p[top].l==i){insert(root[i],root[i],1,n,p[top].r);top++;} 
  }
  for (int i=1;i<=m;i++){
     l=read();r=read();
     if(tp){l=(l+ans-1)%n+1;r=(r+ans-1)%n+1;if (l>r)swap(l,r);}
     ans=query(root[l-1],root[r],1,n,l,r);
     printf("%d\n",ans);
  }
} 


好的,这是一道经典的单调栈问题。题目描述如下: 有 $n$ 个湖,第 $i$ 个湖有一个高度 $h_i$。现在要在这些湖之间挖一些沟渠,使得相邻的湖之间的高度差不超过 $d$。请问最少需要挖多少个沟渠。 这是一道单调栈的典型应用题。我们可以从左到右遍历湖的高度,同时使用一个单调栈来维护之前所有湖的高度。具体来说,我们维护一个单调递增的栈,栈中存储的是湖的下标。假设当前遍历到第 $i$ 个湖,我们需要在之前的湖中找到一个高度最接近 $h_i$ 且高度不超过 $h_i-d$ 的湖,然后从这个湖到第 $i$ 个湖之间挖一条沟渠。具体的实现可以参考下面的代码: ```c++ #include <cstdio> #include <stack> using namespace std; const int N = 100010; int n, d; int h[N]; stack<int> stk; int main() { scanf("%d%d", &n, &d); for (int i = 1; i <= n; i++) scanf("%d", &h[i]); int ans = 0; for (int i = 1; i <= n; i++) { while (!stk.empty() && h[stk.top()] <= h[i] - d) stk.pop(); if (!stk.empty()) ans++; stk.push(i); } printf("%d\n", ans); return 0; } ``` 这里的关键在于,当我们遍历到第 $i$ 个湖时,所有比 $h_i-d$ 小的湖都可以被舍弃,因为它们不可能成为第 $i$ 个湖的前驱。因此,我们可以不断地从栈顶弹出比 $h_i-d$ 小的湖,直到栈顶的湖高度大于 $h_i-d$,然后将 $i$ 入栈。这样,栈中存储的就是当前 $h_i$ 左边所有高度不超过 $h_i-d$ 的湖,栈顶元素就是最靠近 $h_i$ 且高度不超过 $h_i-d$ 的湖。如果栈不为空,说明找到了一个前驱湖,答案加一。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值