【BZOJ4576】[USACO16OPEN] 262144(链表+模拟)

点此看题面

大致题意: 有一排(n)个数,每次可以选两个相邻的数(x),合并为一个(x+1)。求最后能得到的最大值。

前言

由于是通过链表的标签找到了这道题,潜意识里先有了个链表。

因此,对于这道题,我想大佬和我分别会有着两种反应:

  • 大佬:(DP)(DP)(DP)(DP)啊!
  • 我:暴力?((n)好大)贪心?(贪不来)链表?(诶,好像的确是这个标签啊)模拟!(耶,大模拟是最强的)

好吧,说真的,不知道为什么最近做到的总是(DP)题,而且总是不会往(DP)这个方向想,甚至搞出许多莫名其妙的解法。(肯定只是因为我太菜)

所以,接下来我就讲讲我这个没用的模拟题解吧。

模拟

考虑如果两个小数中间存在大数,那么这两个小数永远无法合并(就像牛郎织女隔着银河),但如果这两个小数变大了,或许它们又能相会(没错,就像是鹊桥)。

不过,如果我们从小到大考虑每一个数,若当前数无法再与任何相等的数合并时,它就再也不能合并了(也就是你残忍地断开了鹊桥)。

而且,我们会发现,如果两个大数中间存在小数,那这个小数也就把大数隔开了。也即我们把整个序列看成一个链表,这就相当于链表断成了两个。

因此,我们枚举值(x),然后枚举每一段等于(x)的数,则有:

  • 若段长(t=1):断开链表。
  • 若段长(t)为偶数:则合并成(frac t2)(x+1),重新连接链表。
  • 若段长(t)为奇数:则必然合成(lfloorfrac t2 floor)(x+1),剩下一个(x)。这里貌似要判断究竟怎么把这些(x+1)分给两端(因为(x)的存在必将导致链表断开),可实际上,由于两侧链表是独立的,所以我们大可给两端都加上(lfloorfrac t2 floor)(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;//枚举值
}
原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4576.html