CF1500F Cupboards Jumps
CF1500F Cupboards Jumps
给定正整数 \(n,C\) 和长度为 \(n-2\) 的序列 \(w(0\leq w_i\leq C)\)。
你需要构造一个长度为 \(n\) 的序列 \(h\),满足:
1.对于任意整数 \(i\in[1,n-2]\) 有 \(\max(h_i,h_{i+1},h_{i+2})-\min(h_i,h_{i+1},h_{i+2})=w_i\)。
2.序列 \(h\) 中的任意元素 \(h_i\) 满足 \(0\leq h_i\leq 10^{18}\)。
有解输出YES之后输出任意一个满足要求的序列 \(h\),无解输出NO。
对于 \(100\%\) 的数据,满足 \(3\leq n\leq10^6,0\leq C\leq10^{12}\)。
绝世好题。
首先考虑差分,令 \(d_i=h_{i+1}-h_i\),可以发现 \(w_i=\max \{|d_i|,|d_{i+1}|,|d_i+d_{i+1}|\}\)。
不难发现 \(w_i=|d_i|+|d_{i+1}|\) 当且仅当 \(d_i\) 和 \(d_{i+1}\) 同正同负。这就意味着,如果 \(d_i\) 的一个连续段都是同正或同负的,那么只要确定一个就能确定所有;反之,如果 \(d_i\) 和 \(d_{i+1}\) 异号,那么必然有一个绝对值为 \(w_i\)。
考虑对差分进行 DP,令 \(f_{i,j}\) 表示前 \(i\) 个数,将 \(j\) 作为结尾是否可行。转移如下:
时间复杂度 \(\mathcal{O}(nC)\),考虑优化。
对每一种转移推一波性质:
-
\(f_{i+1,w_i}←f_{i,x}\ (x\leq w_i)\),如果上一层存在 \([x,x](x\leq w_i)\),则在这一层加入区间 \([w_i,w_i]\)。
-
\(f_{i+1,x}←f_{i,w_i}\ (x\leq w_i)\),如果上一层存在 \([w_i,w_i]\),则在这一层加入 \([0,w_i]\)。
-
\(f_{i+1,x}←f_{i,w_i-x}\ (x\leq w_i)\),相当于把上一层的区间以 \(\dfrac{w_i}{2}\) 为中心翻转,再计算一个单点的贡献。但是这样计算很麻烦,不妨考虑每次都对数轴以零点为中心进行翻转,只需额外记录当前翻转次数和平移距离即可。
对于翻转操作,我们考虑使用 \(\texttt{STL::set}\),这样可以使时间复杂度变为 \(\mathcal{O}(n\text{log}_2n)\)。
接下来考虑如何构造合法方案。
我们不妨首先找出任意一个 \(res_i\),表示这个值可以在 \(|d_i|\) 中出现,然后我们考虑如何构造出 \(|d_i|\),考虑从最后一个元素开始,我们令 \(|d_{n+1}|=res_{n+1}\),然后如果 \(|d_{n+1}|=w_n\),那么直接令 \(|d_n|=res_n\),否则如果 \(res_n=w_n\),那我们就令 \(|d_n|=w_n\),否则只好令 \(|d_n|=w_n-|d_{n+1}|\)。然后考虑如何通过 \(|d_i|\) 构造出 \(d_i\),具体方案就是从右向左扫一遍,如果 \(|d_i|+|d_{i+1}|≠w_i\),就将其变号。
点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define N 1000010
int n,m,a[N];
int res[N],del[N];
int L,R,sign,tag;
set <int> st;
int read ()
{
int k=1,s=0;char ch=getchar ();
while (!isdigit (ch)) {if (ch=='-') k=-1;ch=getchar ();}
while (isdigit (ch)) {s=s*10+ch-'0';ch=getchar ();}
return k*s;
}
void Init ()
{
n=read (),m=read ();
L=0,R=m;
for (int i=1;i<=n-2;i++)
{
a[i]=read ();
int limL,limR,now;
if (sign) limL=tag,limR=tag-a[i];
else limL=-tag,limR=a[i]-tag;
now=limR;
if (limL>limR) swap (limL,limR);
L=max (L,limL),R=min (R,limR);
while (st.size () && (*st.begin ())<limL)
st.erase (st.begin ());
while (st.size () && (*st.rbegin ())>limR)
st.erase (*st.rbegin ());
if (!st.size () && L>R)
{
printf ("NO\n");
exit (0);
}
if (st.count (now) || (L<=now && now<=R))
{
L=0,R=a[i],res[i]=a[i];
st.clear ();
sign=0,tag=0;
continue;
}
int pre=(L<=R)?L:*st.begin ();
if (sign) res[i]=tag-pre;
else res[i]=tag+pre;
sign^=1,tag=a[i]-tag;
if (sign) now=tag-a[i];
else now=a[i]-tag;
st.insert (now);
}
}
void Work ()
{
int pre=(L<=R)?L:*st.begin ();
if (sign) del[n-1]=tag-pre;
else del[n-1]=tag+pre;
for (int i=n-2;i>=1;i--)
if (res[i]==a[i]) del[i]=a[i];
else if (del[i+1]==a[i]) del[i]=res[i];
else del[i]=a[i]-del[i+1];
int opt=1;
for (int i=n-2;i>=1;i--)
{
if (abs (del[i])+abs (del[i+1])!=a[i]) opt*=-1;
del[i]=opt*del[i];
}
int low=0;
for (int i=1;i<=n-1;i++)
{
del[i]+=del[i-1];
low=min (low,del[i]);
}
printf ("YES\n");
for (int i=1;i<=n;i++)
printf ("%lld ",del[i-1]-low);
printf ("\n");
}
signed main ()
{
Init ();
Work ();
return 0;
}

浙公网安备 33010602011771号