1. 程式人生 > >【BZOJ3956】Count,單調棧+ST表維護區間最大值

【BZOJ3956】Count,單調棧+ST表維護區間最大值

Time:2016.08.11
Author:xiaoyimi
轉載註明出處謝謝

傳送門
思路:
TA爺眼中的水題
首先有個特別的結論
總共的點對數不會超過2n
因為對於元素i來說,如果只考慮與比它高的元素進行配對
那麼最多左邊一個,右邊一個,再靠左或靠右的就不滿足配對條件了
考試的時候我想到的是單調棧維護一個不上升的序列,但不知道具體並不會做
講題時使用了ST表維護區間最大
(為什麼不用線段樹?因為線段樹常數比較大……而且這裡是靜態查詢,正好是RMQ經典操作)
考慮區間[l,r]
找出[l,r]中最大高度的下標k
顯然l~k-1不能和k之後的元素配對
k+1~r不能和k之前的元素配對
ans=[l,k]在[l,k]中的配對個數+[k,r]在[k,r]中的配對個數
這個是可以字首和維護的
前後各掃一遍
f[i]指i在[1,i]中配對數量
g[i]指i在[i,n]中配對數量
sum1[i]=Σf[j] 1<=j<=i
sum2[i]=Σg[j] i<=j<=n
ans=sum1[r]-sum1[k]+sum[l]-sum2[k]
注意高度相等時的處理,這裡很噁心,我就不細說了ORZ

#include<cstdio>
#include<iostream>
#include<cmath> 
#define M 300003
#define pd(x,y) (a[x]>a[y]?x:y)
using namespace std;
int in()
{
    int t=0;char ch=getchar();
    while (ch<'0'||ch>'9') ch=getchar();
    while (ch>='0'&&ch<='9') t=(t<<1)+(t<<3)+ch-48
,ch=getchar(); return t; } int n=in(),m=in(),tp=in(); int sum1[M],sum2[M],S[M]; int a[M],fa[M][19]; int RMQ(int x,int y) { int t=log2(y-x+1); return pd(fa[x][t],fa[y-(1<<t)+1][t]); } main() { for (int i=1;i<=n;i++) a[i]=in(); for (int i=1;i<=n;i++) { for (;S[0
];S[0]--) { sum1[i]++; if (a[S[S[0]]]>=a[i]) break; } for (;S[0];S[0]--) if (a[S[S[0]]]>a[i]) break; S[++S[0]]=i; sum1[i]+=sum1[i-1]; } S[0]=0; for (int i=n;i>=1;i--) { for (;S[0];S[0]--) { sum2[i]++; if (a[S[S[0]]]>=a[i]) break; } for (;S[0];S[0]--) if (a[S[S[0]]]>a[i]) break; S[++S[0]]=i; sum2[i]+=sum2[i+1]; } for (int i=1;i<=n;i++) fa[i][0]=i; for (int i=1;(1<<i)<=n;i++) for (int j=1;j+(1<<i)-1<=n;j++) fa[j][i]=pd(fa[j][i-1],fa[j+(1<<i-1)][i-1]); int mx,l,r,L,R,lastans=0; for (;m;m--) { l=in();r=in(); if (tp) L=min((lastans+l-1)%n,(lastans+r-1)%n)+1, R=max((l+lastans-1)%n,(r+lastans-1)%n)+1; else L=l,R=r; mx=RMQ(L,R); printf("%d\n",lastans=sum1[R]-sum1[mx]-sum2[mx]+sum2[L]); } }

其實也可以寫主席樹的,但我被相等情況的處理噁心到了,所以沒有寫下去……