1. 程式人生 > >【題解】 bzoj3956: Count (ST表+單調棧)

【題解】 bzoj3956: Count (ST表+單調棧)

n) uri 方案 自己 ace 貢獻 int n+1 思路

題面

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\n",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!!!

【題解】 bzoj3956: Count (ST表+單調棧)