题目大意:
给出n个数a[1..n],n<=262144,a[i]<=40,相邻且相同的数可以合并成一个并将值加1,问能获得的最大数是多少
用一个双向链表维护原数列,每个节点记录此节点对应的数值和数的个数,合并相邻且对应数值相同的节点
每次选一个数值最小的点处理,此时两侧的数都更大
若这个点只有一个数则直接删去并断开两侧,此时两侧的数不可能再互相合并
若这个点有偶数个数则数值+1,个数/2,检测能否和两侧合并
若这个点有奇数个数,两侧的数也不可能再互相合并了,因此将这个点分裂成两个互不相连的点,数值+1,(个数-1)/2,分别与两侧连接
可以证明时空复杂度均为O(n)
#include<cstdio> #include<vector> inline int input(){ int x=0,c=getchar(); while(c>57||c<48)c=getchar(); while(c>47&&c<58)x=x*10+c-48,c=getchar(); return x; } const int N=1000000; std::vector<int>q[128]; int nx[N],pv[N],v[N],t[N],p=0,ans=0,n; int main(){ n=input(); for(int i=1,a=-1,b;i<=n;i++){ b=input(); if(a!=b)v[++p]=b; ++t[p]; a=b; } for(int i=1;i<=p;i++){ pv[i]=i-1; nx[i]=i+1; q[v[i]].push_back(i); } nx[p]=0; for(int i=1;i<100;i++){ for(int j=0,sz=q[i].size();j<sz;j++){ int w=q[i][j]; if(!t[w])continue; if(t[w]==1){ nx[pv[w]]=pv[nx[w]]=0; t[w]=0; }else if(t[w]&1){ int _t=t[w]>>1,_pv=pv[w],_nx=nx[w],_v=v[w]+1; t[w]=0; if(v[_pv]==_v){ t[_pv]+=_t; pv[_nx]=nx[_pv]=0; }else{ nx[_pv]=++p; pv[p]=_pv; t[p]=_t; v[p]=_v; q[_v].push_back(p); } if(v[_nx]==_v){ t[_nx]+=_t; pv[_nx]=nx[_pv]=0; }else{ pv[_nx]=++p; nx[p]=_nx; t[p]=_t; v[p]=_v; q[_v].push_back(p); } }else{ int _t=t[w]>>1,_pv=pv[w],_nx=nx[w],_v=v[w]+1; t[w]=0; if(v[_pv]==_v){ if(v[_nx]==_v){ t[_pv]+=t[_nx]+_t; t[_nx]=0; pv[nx[_pv]=nx[_nx]]=_pv; }else{ t[_pv]+=_t; pv[_nx]=_pv; nx[_pv]=_nx; } }else if(v[_nx]==_v){ t[_nx]+=_t; pv[_nx]=_pv; nx[_pv]=_nx; }else{ t[w]=_t; v[w]=_v; q[_v].push_back(w); } } } } for(int i=1;i<=p;i++)if(v[i]>ans)ans=v[i]; printf("%d",ans); return 0; }
时间: 2024-11-12 09:03:15