涼宮春日的嘆息 [思維+二分]
阿新 • • 發佈:2020-08-14
涼宮春日的嘆息
挺好的思維題。
題目描述
給定一個數組,將其所有子區間的和從小到大排序,求第 \(k\) 小的是多少。
輸入格式
第一行兩個數 \(n\) , \(k\) ,表示陣列的長度和 \(k\);
第二行有 \(n\) 個數,第\(i\)個是\(a[i]\),表示給定的陣列。
輸出格式
僅一個數,表示答案。
樣例
樣例輸入 #1
5 6
1 1 1 1 1
樣例輸出 #1
2
樣例輸入 #2
8 20
2 3 1 2 5 3 2 3
樣例輸出 #2
8
資料範圍與提示
對於\(15\%\)的資料,\(n\leqslant 1000\)
對於\(30\%\)的資料,\(n\leqslant 5000\)
對於\(50\%\)的資料,\(n,k\leqslant 10^5\)
對於\(70\%\)的資料,\(n\leqslant 10^5\)
對於\(100\%\)的資料,\(n\leqslant 10^6,1\leqslant a[i],k\leqslant 10^9\)
分析
思路挺難看出來的,但是看出來就很好寫了。
首先我們看到 \(k\) 的範圍是 \(k\leqslant 10^9\) ,那麼直接列舉前 \(k\) 大肯定是不行的,陣列都開不下。但是最後的目的還是要去找到第 \(k\) 小的那個數,那麼我們可以換一種列舉的方式:列舉值。
列舉排名不現實,所以我們就可以列舉當前第 \(k\) 小的可能值,然後判斷一下他的排名到底是比 \(k\)
求第幾大的時候我們只需要列舉右端點,然後雙指標搞一下就行。
程式碼
#include<cstdio> #include<cstring> #include<iostream> #include<algorithm> using namespace std; const int maxn = 1e6+10; #define ll long long ll a[maxn]; int n,k; ll jud(ll mid){//判斷當前值排名第幾 ll ans = 0; int j = 0; for(int i=1;i<=n;++i){ while(a[i] - a[j] > mid)j++;//列舉右端點,更新雙指標 ans += i - j;//記錄排名 } return ans; } int main(){ freopen("A.in","r",stdin); freopen("A.out","w",stdout); scanf("%d%d",&n,&k); for(int i=1;i<=n;++i){ scanf("%lld",&a[i]); a[i] += a[i-1]; } ll l = 0,r = a[n]; while(l < r){//二分答案 ll mid = l + r >> 1; if(jud(mid) >= k)r = mid;//排名比k大就縮小值 else l = mid + 1;//否則增加 } printf("%lld\n",l); return 0; }