• 【题解】 bzoj3956: Count (ST表+单调栈)


    题面

    Solution

    • 看了一点点题解,自己又刚了(2h30min),刚了出来qwq,我好菜啊qwq
    • 貌似这道题是BZOJ 4826的弱化,弱化都不会qwq凉凉
    • Solution
    • 首先你可以考虑,找出([l,r])的最大值((x))后,你会发现这个好的点对,是不会跨过这最大值((x))的,那么答案就是([l,x])的所有点作为点对左端的方案数(+) ([x+1,r])所有点作为点对右端的方案数
    • 求方案数我们就可以用单调栈(O(n))求出来,然后做一次前缀和就好了
    • 这个单调栈操作我觉得这个贼鸡儿难,调了很多特殊数据终于过了
    • 具体操作:

    相邻的两个值(a,b)不论怎样肯定是一个点对,如果单调栈处理,因为我们要处理出作为左端点的方案数和右端段的方案数,相邻的这种点对就会在(a)为左端点时加一次,在(b)为右端点的时候加一次,这样会有重复,比较麻烦,我们就不考虑这种相邻的情况(这样最后的答案就是([l,x-1])的所有点作为点对左端的方案数加 ([x+1,r])所有点作为点对右端的方案数,因为(x)最大只会是与相邻两个成点对)
    现在假定我们求每个点为点对左端点的方案数
    后面的操作就是:我们维护一个单调上升的栈,每入队一个点,如果这个点比队尾大,队尾的这个点如果不是现在加入的点的相邻点,我们就把他的方案数(+1),然后弹出(因为你想,如果这个点不相邻,中间的全部被弹出,说明一定不会大于这两边的两个)
    稍微特殊的情况就是,如果队尾的值(假设为(a_1))与现在加入的值(假设为(a_2))相等,那么我们就给队尾的方案数(+1),然后弹出,并且把这个新加入的点作为队尾。((*)解释在后面)
    ((*))解释:因为题目要求是大于,所以中间存在与两边等于的情况也是不行的,所以(a_2)不会对(a_1)之前的点有贡献,(a_1)不会对(a_2)之后的点有贡献。
    然后弹完队尾后,我们把这个现在的队尾的方案数加一,原因同上,他们两之间的数一定小于两边,然后把点入队即可
    最后没弹出队列的点不用管,因为他们单调递增,比如(a,b,c)(a<b<c)),后面不会再有点与(a)形成点对,因为(b,c)一定大于(a)
    右端点的反着来一边就好了

    • 最后计算答案记得加上相邻的方案数即可

    • 那个查询最大值可以用(ST表),也可以用线段树,(本来是要练习(ST)表的,结果调单调栈调了好久qwq

    • 感觉这道题还是挺神的吧,把思路理了一边总算清楚很多了.

    Code

    • 调了好久,写的丑
    //It is coded by ning_mew on 7.19
    #include<bits/stdc++.h>
    #define LL long long
    using namespace std;
    
    const int maxn=3e5+7,inf=1e9+7;
    
    int n,m,Type;
    LL sl[maxn],sr[maxn],two[25],lastans=0;
    int team[maxn],s[maxn],tl=1,ST[maxn][25],Log[maxn];
    
    int maxa(int x,int y){if(s[x]>s[y])return x;return y;}
    int quary(int l,int r){
      int k=Log[r-l+1];
      return maxa(ST[l][k],ST[r-two[k]+1][k]);
    }
    void pre(){
      two[0]=1; 
      for(int i=2;i<=n;i++)Log[i]=Log[i>>1]+1;
      for(int i=1;i<=16;i++)two[i]=two[i-1]*2;
      for(int j=1;j<=16;j++)
      for(int i=1;i+two[j-1]<=n;i++){
        ST[i][j]=maxa(ST[i][j-1],ST[i+two[j-1]][j-1]);
      }
    }
    void work(int l,int r){
      int k=quary(l,r);
      lastans=sl[k-1]-sl[l-1]+sr[r]-sr[k]+r-l;
      printf("%lld
    ",lastans);
      if(!Type)lastans=0;return;
    }
    int main(){
      scanf("%d%d%d",&n,&m,&Type);
      for(int i=1;i<=n;i++)scanf("%d",&s[i]),ST[i][0]=i;
      pre();
      for(int i=1;i<=n;i++){
        while(tl>1){
          if(s[ team[tl-1] ]<=s[i]){
    	if(team[tl-1]!=i-1)sl[team[tl-1]]++;
    	if(s[team[tl-1]]==s[i]){break;}
    	tl--;
          }
          else break;
        }
        if(s[team[tl-1]]==s[i]){team[tl-1]=i;continue;}
        if(team[tl-1]!=i-1)sl[team[tl-1]]++;
        team[tl]=i;tl++;
      }
      
      memset(team,0,sizeof(team));tl=1;
      for(int i=n;i>=1;i--){
        while(tl>1){
          if(s[ team[tl-1] ]<=s[i]){
    	if(team[tl-1]!=i+1)sr[team[tl-1]]++;
    	if(s[team[tl-1]]==s[i]){break;}
    	tl--;
          }
          else break;
        }
        if(s[team[tl-1]]==s[i]){team[tl-1]=i;continue;}
        if(team[tl-1]!=i+1)sr[team[tl-1]]++;
        team[tl]=i;tl++;
      }
    
      sl[0]=sr[n+1]=0;
      for(int i=1;i<=n;i++)sl[i]=sl[i]+sl[i-1],sr[i]=sr[i]+sr[i-1];
      
      lastans=0;
      for(int i=1;i<=m;i++){
        LL l,r;scanf("%lld%lld",&l,&r);
        l=(l+lastans-1)%n+1;r=(r+lastans-1)%n+1;if(l>r)swap(l,r);
        work(l,r);
      }return 0;
    }
    
    

    博主蒟蒻,随意转载。但必须附上原文链接:http://www.cnblogs.com/Ning-Mew/,否则你会场场比赛暴0!!!

  • 相关阅读:
    (原创)c#学习笔记10--定义类成员01--成员定义06--自动属性
    (原创)c#学习笔记10--定义类成员01--成员定义03--定义属性
    (原创)c#学习笔记10--定义类成员01--成员定义02--建立方法
    【BZOJ3110】K大数查询(ZJOI2013)-整体二分+线段树
    【BZOJ3110】K大数查询(ZJOI2013)-整体二分+线段树
    【HDU5412】CRB and Queries-整体二分:带修改区间第K小
    【HDU5412】CRB and Queries-整体二分:带修改区间第K小
    【NOI2007T2】货币兑换-DP斜率优化+CDQ分治
    【NOI2007T2】货币兑换-DP斜率优化+CDQ分治
    【BZOJ3295】动态逆序对(CQOI2011)-CDQ分治:三维偏序
  • 原文地址:https://www.cnblogs.com/Ning-Mew/p/9338515.html
Copyright © 2020-2023  润新知