HDU5654xiaoxin and his watermelon candy 离线+树状数组

题意:bc 77div1 d题(中文题面),其实就是询问一个区间有多少不同的三元组,当然这个三元组要符合条件

分析(先奉上官方题解)

首先将数列中所有满足条件的三元组处理出来,数量不会超过 nn个。

设 pre[i] 为第 i 个三元组前一次出现的位置,如果在前面没有出现过则设为0,对于不合法的三元组,pre[i]设为 n。

这样对于一个查询 [L, R], 我们只不需要计算出 在这个区间内有多少个三元组的 pre 值是小于 L 的。

到这里,就可以使用可持久化线段树来计算了。

-------------------------------------------------------------华丽的分割线

然后蒟蒻表示不会这种在线的主席树做法,主席树据说很好,马上去学习

然后介绍本蒟蒻的离线做法,其实都是套路,就是求一个区间包含了,多少个小区间(即三元组)

首先和题解一样,找到符合条件的三元组,记录它的序号id,这个三元组的每个元素,他的右边界

然后按照三元组排序,以及他的id,这样处理每个三元组的pre,即在他左边,与他相同的三元组的id

然后恢复下顺序

然后就是离线的套路,把查询区间按照右端点排序,更新所有三元组右边界小于他的左端点,然后区间求和,就是答案

但是这个题有一个点,要避免查询重复,所以在当前查询的时候,要保证没有重复,所以更新三元组时,要把他的pre删掉(因为相同)

这样对于重复的三元组,只保留最靠近当前查询区间右端的

代码:

#include<cstdio>
#include<cstring>
#include<queue>
#include<cstdlib>
#include<algorithm>
#include<vector>
#include<cmath>
using namespace std;
typedef long long LL;
const int N=2e5+5;
const int INF=0x3f3f3f3f;
struct Node{
  int a,b,c,id,pre,r;
  bool operator==(const Node &rhs)const{
     if(a==rhs.a&&b==rhs.b&&c==rhs.c)
      return 1;
      return 0;
  }
}o[N];
int n,T,q;
bool cmp1(Node x,Node y){
  if(x.a!=y.a)
    return x.a<y.a;
  if(x.b!=y.b)
    return x.b<y.b;
  if(x.c!=y.c)
    return x.c<y.c;
  return x.id<y.id;
}
bool cmp2(Node x,Node y){
  return x.id<y.id;
}
int d[N],ans[N];
struct Q{
  int l,r,id;
  bool operator<(const Q &rhs)const{
     return r<rhs.r;
  }
}p[N];
int bit[N];
void add(int x,int t){
  for(int i=x;i<=n;i+=i&(-i))
     bit[i]+=t;
}
int get(int x){
  int ans=0;
  for(int i=x;i>0;i-=i&(-i))
     ans+=bit[i];
  return ans;
}
int main(){ 
    scanf("%d",&T);
    while(T--){
      scanf("%d",&n);
      int cnt=0;
      for(int i=1;i<=n;++i){
        scanf("%d",&d[i]);
        if(i<=2)continue;
        if(d[i]>=d[i-1]&&d[i-1]>=d[i-2]){
           ++cnt;
           o[cnt].a=d[i-2],o[cnt].b=d[i-1];
           o[cnt].c=d[i],o[cnt].pre=-1;
           o[cnt].r=i,o[cnt].id=cnt;
        }
      }
      sort(o+1,o+1+cnt,cmp1);
      for(int i=2;i<=n;++i){
        if(o[i]==o[i-1]){
          o[i].pre=o[i-1].id;
        }
      }
      sort(o+1,o+1+cnt,cmp2);
      scanf("%d",&q);
      for(int i=1;i<=q;++i){
        scanf("%d%d",&p[i].l,&p[i].r);
        p[i].id=i;
      }
      sort(p+1,p+1+q);
      int pos=1;
      memset(bit,0,sizeof(bit));
      for(int i=1;i<=q;++i){
        for(;pos<=cnt&&o[pos].r<=p[i].r;++pos){
          int pre=o[pos].pre;
          if(pre!=-1)
            add(o[pre].r-2,-1);
          add(o[pos].r-2,1);
        }
        ans[p[i].id]=get(p[i].r)-get(p[i].l-1);
      }
      for(int i=1;i<=q;++i)
        printf("%d\n",ans[i]);
    }
    return 0;
}
View Code

 

转载于:https://www.cnblogs.com/shuguangzw/p/5335094.html

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值