HihoCoder - 1483 区间最值
生活随笔
收集整理的這篇文章主要介紹了
HihoCoder - 1483 区间最值
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
給定n個數A1...An,小Ho想了解AL..AR中有多少對元素值相同。小Ho把這個數目定義為區間[L,R]的價值,用v[L,R]表示。
例如1 1 1 2 2這五個數所組成的區間的價值為4。
現在小Ho想知道在所有的的v[L,R](1 <= L <= R <= n)中,第k小的值是多少。
Input第一行一個數T(T<=10),表示數據組數。
對于每一組數據:
第一行兩個數n,k(1<=n<=200,000,1<=k<=n*(n+1)/2)
第二行n個數A1…An(1<=Ai<=1,000,000,000)
Output一個數表示答案。
Sample Input
2 4 7 1 1 2 3 3 6 100 100 100Sample Output
0 3題意:我們給出n個數,我們求任意一段區間,他們相同的數的次數就是區間的值,然后我們按值排序求第k個區間的值是多少
思路:開始我用的n2,果斷超時。。然后我們看到ai的范圍有這么大,我們又要記錄次數,顯然我們可以用map,但是我用map也超時了,
所以有個高級的操作,因為n的范圍數組能開的下,只是ai值大而已,所以我們可以離散化,然后我們想一下,怎么求答案呢,如果我們直接求出所有的區間再排序輸出的話n2復雜度
所以發現不行,我們仔細想想,我們能得知我們區間長度越小,我們的區間值肯定更小,我們可以二分去處理,二分的話最小值是0沒有一個相同,最大的時候也就是全部的數都相同,可以推出是n*(n-1)/2
因為我們要求是求第k個區間的值,那么我們就只要去尋找判斷,小于當前數的區間個數有多少個,如果小于這個數的區間比k還大的話,說明我們當前的數肯定比我們要求的小,所以我們向右擴展,反之亦然
然后我們想如何去求多少個區間比他小呢?
我們可以不用求出所有區間的值為什么呢,因為我們區間的個數和值的大小息息相關
如果[l.r]是比k小的,那么[l,r-1],[l,r-2]....[l,l]都是小于k的數,這里就用到了我們的尺取法
那么我們就把它變成了一個nlogn的算法
#include<cstdio> #include<cmath> #include<cstring> #include<map> #include<algorithm> using namespace std; typedef long long ll; ll a[200001]; ll t,n,m,temp[200001]; ll vis[200001]; ll check(ll mid)//尺取求比mid小的區間個數 {int i,j;ll sum=0;ll num=0;memset(vis,0,sizeof(vis));for(i=0,j=0;i<n;i++){for(;j<n&&sum+vis[a[j]]<=mid;j++){sum+=vis[a[j]];vis[a[j]]++;}num+=j-i;//尺取思想核心vis[a[i]]--;sum-=vis[a[i]];}return num>=m; } int main() {ll ans;scanf("%lld",&t);while(t--){scanf("%lld%lld",&n,&m);for(int i=0;i<n;i++){scanf("%lld",&a[i]);temp[i]=a[i];}int cnt;sort(temp,temp+n);//離散化cnt = unique(temp,temp+n) - temp;for(int i = 0 ; i < n ; ++i)a[i] = lower_bound(temp,temp+cnt,a[i]) - temp;ll left=0,right=((ll)n*((ll)n-1))/2;while(left<=right){ll mid=(left+right)/2;if(check(mid))//如果小于mid的區間個數比m多的話,說明值還不夠小{ans=mid;right=mid-1;}else{left=mid+1;}}printf("%lld\n",ans);} }
?
轉載于:https://www.cnblogs.com/Lis-/p/9393788.html
總結
以上是生活随笔為你收集整理的HihoCoder - 1483 区间最值的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 第十周补做作业
- 下一篇: 使用exp导出导入,需要注意的问题。