zoukankan      html  css  js  c++  java
  • 【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;//枚举值
    }
    
  • 相关阅读:
    css表格单元格间距设置
    JavaScript(js)设置输入焦点(focus)
    让div居中的方法
    Window.open()的使用
    getElementsByTagName的用法
    offsetTop获取top值
    js中indexof的使用
    jquery解析json数据
    iframe的使用
    WCF学习笔记Ⅲ
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/BZOJ4576.html
Copyright © 2011-2022 走看看