My Blogs
[ABC221G] Jumping sequence
做法有点深刻,优化方式非常深刻。
首先是哈密顿距离和切比雪夫距离的转化:把坐标系旋转四十五度,变成每次向左上,右上,左下,右下走 的距离,要求最后走到 。然后这样可以对于横纵坐标分开做了(非常神奇)。
问题转化成了询问是否存在序列 满足 。先钦定全都是 或者 之后可以变形为,询问是否能从 中选择若干个数使得和为 。可以直接 bitset
做到 ,但是存在更优秀的做法。
首先从前向后找到最大的 满足 。然后如果存在解,则一定是前面删掉若干个元素然后后面再加进来若干个元素。可以设 表示从 中选择一些负数, 中选择一些正数,能否凑出来 。
一个关键性质是 只需要保留 的部分,其中 是题目中数列的值域。证明比较简单,对于一种方案,假设选取的左侧负数是 ,右侧选取的正数是 ,如果当前 是正的就在左边加负数,否则在右边加正数。如果有一个序列取空了剩下的就直接全取了。因为 ,所以上面这样值域不会超过 。
但是 的状态就是 的。注意到如果固定了 ,合法的 是一段前缀。可以记录 表示当前右端点是 ,值是 ,合法的最大左端点是 。转移:
现在复杂度瓶颈在于第三种转移。但是可以发现许多第三种转移都是没用的:如果 ,则可以在 的时候就转移掉这个 。所以每次只需要转移新添进候选值的 ,即 。因为 单调不降,对于一个 , 的枚举次数即为 ,所以这样总复杂度是 。输出方案也比较平凡,只需要记录从哪个状态转移过来的即可。
int n,A,B,a[2010],f[2010][4010];
pii from[2010][4010];
bitset<2010> v1,v2;
void solve(int X)
{
if(X<0)puts("No"),exit(0);
int v=0,I=0;memset(f,0,sizeof(f));
while(I<n&&v+a[I+1]<=X)v+=a[++I];
if(I==n&&v<X)puts("No"),exit(0);
v=X-v,f[I][2000]=I+1,v1.reset();
for(int i=I+1;i<=n;++i)
{
for(int j=4000;j>=0;--j)
{
if(Mmax(f[i][j],f[i-1][j]))from[i][j]=mp(i-1,j);
if(j>a[i])if(Mmax(f[i][j],f[i-1][j-a[i]]))
from[i][j]=mp(i-1,j-a[i]);
for(int k=f[i-1][j];k<f[i][j];++k)if(j>=a[k])
if(Mmax(f[i][j-a[k]],k))from[i][j-a[k]]=mp(i,j);
}
}
if(!f[n][2000+v])puts("No"),exit(0);
for(int i=1;i<=I;++i)v1[i]=1;
int i=n,j=2000+v,x,y;
while(i!=I||j!=2000)
{
x=from[i][j].fi,y=from[i][j].se;
if(x==i)v1[f[i][j]]=0;
else if(y!=j)v1[i]=1;
i=x,j=y;
}
}
inline void mian()
{
read(n,A,B),A=A+B,B=A-B-B;
for(int i=1;i<=n;++i)read(a[i]),A+=a[i],B+=a[i];
if((A&1)||(B&1))return puts("No"),void();
solve(A>>1),v2=v1,solve(B>>1),puts("Yes");
for(int i=1;i<=n;++i)
{
if(v1[i]==v2[i]){if(v1[i])putchar('R');else putchar('L');}
else{if(v2[i])putchar('U');else putchar('D');}
}
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?