簡要題意:
給定一個 ~ 的排列,求以 爲中位數的 連續子序列且長度爲奇數 的個數。
顯然這段序列包含 .
中位數的定義:排序後在最中間的數。
算法一
對於 的數據,.
由於這段序列一定包含 ,那麼我們可以枚舉區間 包含 (有類似於雙指針),然後單獨取出 這段進行排序,暴力判斷即可。
時間複雜度:.
實際得分:.
算法二
對於 的數據,.
顯然我們不需要每次都把 這段取出,可以一次次擴展。
比方說枚舉 (從 到 , 是 的位置),然後枚舉 從 到 . 對於每個 ,只需在原來數組的基礎上添上一個 即可;如果 的話,添加之後要把數組清空。
那麼每次只需要插入一個數的話,我們可以用 插入排序,因爲已經排序的是有序的,因爲插入的位置可以用二分算出。插入操作我們不用數組維護,用 維護會方便很多。
時間複雜度:.
實際得分:.
算法三
對於 的數據,.
拋開排序過程,我們想:因爲排列的性質,不存在重複數。所以,一個連續序列的中位數爲 當且僅當比 大的數的個數和比 小的數的個數相等。 那麼,對於 的左邊,線性 ,用 表示 ~ 比 大的數的個數, 是小的,同理 的右邊也推一遍。
那麼,我們枚舉左右端點 只需要 判斷即可。即 .
時間複雜度:.
實際得分:.
算法四
對於 的數據,.
從算法三的 上入手,我們發現, 等價於 . 所以我們只需要算出 比 大的數的個數與比 小的數的個數之差 重新作爲 數組的狀態,然後枚舉端點即可。
時間複雜度:.
實際得分:.
算法五
對於 的數據,.
從算法四上再優化一下,其實對於固定的一個左端點 ,我們只需要算出有多少個 且 即可。
也就是說,我們需要維護 區間查詢相等個數。
智商不夠,數據結構來湊。所以這個查詢我們可以用 或者 來實現。(隨便用個平衡樹板子都能實現的)
時間複雜度:.
實際得分:.
算法六
對於 的數據,.
從算法五上入手,你發現 區間查詢相等個數 是靜態查詢,不需要修改。那你可以用 權值線段樹(主席樹) 解決本題。
時間複雜度:.
實際得分:.
算法七
對於 的數據,.
在算法六的基礎上,你發現不僅是靜態,而且是固定的一個區間 . 那麼我們不需要用 權值線段樹(可以理解爲 棵線段樹),只需要 棵線段樹即可。
時間複雜度:.
實際得分:.
算法八
對於 的數據,.
如果你的程序在算法七止步不前,只能說明你是個天才,離最後的成功只差幾步了。
固定區間維護靜態相等個數,聽上去很高大上啊,其實不就是個 嗎?
某同學:我還能 %$#^%*(%&))
嗯,改成 之後感覺簡單多了,是不是?然後我們直接省去 數組,直接存入 .
時間複雜度:.
實際得分:.
//爲了看起來清晰 , 用了嵌套三目運算符
// q[tot+=((a[i]>b)?1:(a[i]<b)?-1:0)]++; 其實相當於這幾句:
// if(a[i]>b) tot++;
// if(a[i]<b) tot--;
// q[tot]++;
#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1e5+1;
inline int read(){char ch=getchar();int f=1;while(ch<'0' || ch>'9') {if(ch=='-') f=-f; ch=getchar();}
int x=0;while(ch>='0' && ch<='9') x=(x<<3)+(x<<1)+ch-'0',ch=getchar();return x*f;}
int n,b,wz,a[N];
int tot,sum; ll ans=0;
map<int,int> q;
int main(){
n=read(),b=read();
for(int i=1;i<=n;i++) a[i]=read(),wz=(a[i]==b)?i:wz;
for(int i=wz;i<=n;i++) q[tot+=((a[i]>b)?1:(a[i]<b)?-1:0)]++;
for(int i=wz;i>=1;i--) ans+=q[0-(sum+=(a[i]>b)?1:((a[i]<b)?-1:0))];
printf("%lld\n",ans);
return 0;
}
算法九
對於 的數據,.
(實際上是本人的一個加強)
算法八的基礎上,我們可以嘗試拿掉這個 .
但是你很快發現下標雖然不超過 ,但是會有負數,可能有 .
顯然,哈希處理負下標 是這題的最終正解。
把每個下標都加上 ,解決負下標之後 查詢,拿掉 還解決了 .
時間複雜度:.
實際得分:.