[UVa11235]Frequent values
阿新 • • 發佈:2017-08-05
相同 names d+ clas 可能 拼接 出現的次數 排列 -s
題目大意:給一個非降序排列的整數數組a,你的任務是對於一系列詢問(i, j),回答ai,ai+1...aj中次數出現最多的值所出現的次數。
解題思路:由於是非降序排列,所有相同的數都是連在一起的。
本題可用RMQ做,但是我不會啊。 其實這題可以直接用線段樹做(什麽?RMQ可以用線段樹做?我還是不會啊),不過需要保存三個東西,該區間出現最多的數出現的次數,該區間最左邊的數出現的次數,該區間最右邊出現的次數。
為什麽要保存左邊和右邊的出現次數呢?例如區間$[1,10]$分為區間$[1,5]$和$[6,10]$,那麽$[1,5]$的右邊就有可能和$[6,10]$的左邊拼成一段出現次數更多的序列,所以要保存這兩個值,並計算兩個區間拼接後的最大值和原最大值哪個更優。查詢時同理。
C++ Code:
#include<iostream> using namespace std; int n,m,a[100001]; struct node{ int Ls,Rs,s; node(int l=0,int r=0,int s=0):Ls(l),Rs(r),s(s){} }d[400004]; inline int max(int a,int b){return(a>b)?a:b;} void bt(int l,int r,int o){ if(l==r){ d[o]=node(1,1,1); return; } int mid=l+r>>1; bt(l,mid,o<<1); bt(mid+1,r,o<<1|1); d[o].s=max(d[o<<1].s,d[o<<1|1].s); if(a[mid]==a[mid+1])d[o].s=max(d[o].s,d[o<<1].Rs+d[o<<1|1].Ls); //如果左子區間的最右邊的值等於右子區間的最左邊的值,那麽兩邊就能拼起來,形成一個更長的段 d[o].Ls=d[o<<1].Ls; d[o].Rs=d[o<<1|1].Rs; if(a[mid]==a[mid+1]){ if(d[o].Ls==mid-l+1) d[o].Ls+=d[o<<1|1].Ls; //如果左子區間的最右邊的值等於右子區間的最左邊的值,而左子區間剛好都是一個數,那麽就能和右邊拼起來 if(d[o].Rs==r-mid) d[o].Rs+=d[o<<1].Rs; //右子區同理 } } node query(int l,int r,int o,const int L,const int R,int ll,int rr){ if(L<=l&&r<=R)return d[o]; int mid=l+r>>1; node ld,rd,nd; if(L<=mid)ld=query(l,mid,o<<1,L,R,ll,mid); if(mid<R)rd=query(mid+1,r,o<<1|1,L,R,mid+1,rr); if(ld.s==0)return rd; if(rd.s==0)return ld; nd.s=max(ld.s,rd.s); if(a[mid]==a[mid+1]) nd.s=max(nd.s,ld.Rs+rd.Ls); nd.Ls=ld.Ls; nd.Rs=rd.Rs; if(a[mid]==a[mid+1]){ if(nd.Ls==mid-ll+1) nd.Ls+=rd.Ls; if(nd.Rs==rr-mid) nd.Rs+=ld.Rs; } return nd; } int main(){ ios::sync_with_stdio(false); while(cin>>n>>m){ for(int i=1;i<=n;++i)cin>>a[i]; bt(1,n,1); while(m--){ int x,y; cin>>x>>y; cout<<query(1,n,1,x,y,x,y).s<<endl; } } return 0; }
[UVa11235]Frequent values