【BZOJ4576】[USACO16OPEN] 262144(链表+模拟)
大致题意: 有一排\(n\)个数,每次可以选两个相邻的数\(x\),合并为一个\(x+1\)。求最后能得到的最大值。
模拟
考虑如果两个小数中间存在大数,那么这两个小数永远无法合并,但如果这两个小数变大了,或许它们又能相会。
不过,如果我们从小到大考虑每一个数,若当前数无法再与任何相等的数合并时,它就再也不能合并了。
而且,我们会发现,如果两个大数中间存在小数,那这个小数也就把大数隔开了。也即我们把整个序列看成一个链表,这就相当于链表断成了两个。
因此,我们枚举值\(x\),然后枚举每一段等于\(x\)的数,则有:
- 若段长\(t=1\):断开链表。
- 若段长\(t\)为偶数:则合并成\(\frac t2\)个\(x+1\),重新连接链表。
- 若段长\(t\)为奇数:则必然合成\(\lfloor\frac t2\rfloor\)个\(x+1\),剩下一个\(x\)。这里貌似要判断究竟怎么把这些\(x+1\)分给两端(因为\(x\)的存在必将导致链表断开),可实际上,由于两侧链表是独立的,所以我们大可给两端都加上\(\lfloor\frac t2\rfloor\)个\(x+1\),这样就完美解决了这个问题。
于是就这样模拟即可求出答案。
顺便说一下,这种模拟在值域大的情况下应该也能搞(我才不会说只是因为我一开始没看到值域这么小。。。),只是要记录每个值有哪些位置,而且可能还要搞些离散化之类的鬼东西,有点麻烦,就没去具体实现了。
代码
#include<bits/stdc++.h>
#define Tp template<typename Ty>
#define Ts template<typename Ty,typename... Ar>
#define Reg register
#define RI Reg int
#define Con const
#define CI Con int&
#define I inline
#define W while
#define N 262144
#define pb push_back
using namespace std;
int n,ans,pre[N+5],nxt[N+5],Vt,V[N+5],del[N+5];
struct data {int v,t;I data(CI a=0,CI b=0):v(a),t(b){}}s[N+5];
class FastIO
{
private:
#define FS 100000
#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
#define tn (x<<3)+(x<<1)
#define D isdigit(c=tc())
char c,*A,*B,FI[FS];
public:
I FastIO() {A=B=FI;}
Tp I void read(Ty& x) {x=0;W(!D);W(x=tn+(c&15),D);}
}F;
I void Travel(CI x,CI v)//遍历链表
{
RI i,k,t,L,R;for(i=x;1<=i&&i<=n;i=nxt[i]) if(s[i].v==v)//如果值等于当前值
{
for(ans=v,t=0,L=pre[i];s[i].v==v;i=nxt[i]) t+=s[i].t,//求出一段
nxt[pre[i]]=nxt[i],pre[nxt[i]]=pre[i],del[V[++Vt]=i]=1;R=i;//从链表中除去
if(t==1) {nxt[L]=n+1,pre[R]=0;continue;}//只有一个数,无法合并,断开
t&1?//奇数
(
L&&(s[k=V[Vt--]]=data(v+1,t>>1),nxt[pre[k]=L]=k,nxt[k]=n+1,del[k]=0),//左边
R<=n&&(s[k=V[Vt--]]=data(v+1,t>>1),pre[nxt[k]=R]=k,pre[k]=0,del[k]=0),//右边
!L&&R>n&&(s[k=V[Vt--]]=data(v+1,t>>1),pre[k]=del[k]=0,nxt[k]=n+1)//断开
):
(
s[k=V[Vt--]]=data(v+1,t>>1),pre[k]=del[k]=0,nxt[k]=n+1,//尽可能合并
L&&(nxt[pre[k]=L]=k),R<=n&&(pre[nxt[k]=R]=k)//连在一起
);
}
}
int main()
{
RI i,j,x;for(F.read(n),i=1;i<=n;++i) F.read(x),s[i]=data(x,1),pre[i]=i-1,nxt[i]=i+1;//初始化链表
for(i=1;i^60;++i) for(j=1;j<=n;++j) !del[j]&&!pre[j]&&(Travel(j,i),0);return printf("%d",ans),0;//枚举值
}
待到再迷茫时回头望,所有脚印会发出光芒