[ABC221G] Jumping sequence

[ABC221G] Jumping sequence

My Blogs

[ABC221G] Jumping sequence

做法有点深刻,优化方式非常深刻。

首先是哈密顿距离和切比雪夫距离的转化:把坐标系旋转四十五度,变成每次向左上,右上,左下,右下走 2ai 的距离,要求最后走到 (A+B,AB)。然后这样可以对于横纵坐标分开做了(非常神奇)。

问题转化成了询问是否存在序列 x 满足 i,xi{1,1},A=xiai。先钦定全都是 +1 或者 1 之后可以变形为,询问是否能从 a 中选择若干个数使得和为 A。可以直接 bitset 做到 O(nV2w),但是存在更优秀的做法。

首先从前向后找到最大的 mid 满足 jmidajA。然后如果存在解,则一定是前面删掉若干个元素然后后面再加进来若干个元素。可以设 fl,r,i 表示从 [l,mid] 中选择一些负数,[mid+1,r] 中选择一些正数,能否凑出来 i

一个关键性质是 i 只需要保留 [V,V] 的部分,其中 V 是题目中数列的值域。证明比较简单,对于一种方案,假设选取的左侧负数是 x1xk,右侧选取的正数是 y1ys,如果当前 i 是正的就在左边加负数,否则在右边加正数。如果有一个序列取空了剩下的就直接全取了。因为 AjmidajV,所以上面这样值域不会超过 [V,V]

但是 f 的状态就是 O(nV2) 的。注意到如果固定了 i,r,合法的 l 是一段前缀。可以记录 gi,j 表示当前右端点是 i,值是 j,合法的最大左端点是 gi,j。转移:

gi+1,jgi,jgi+1,j+aigi,jgi+1,jakkkgi+1,j

现在复杂度瓶颈在于第三种转移。但是可以发现许多第三种转移都是没用的:如果 kgi,j,则可以在 i1 的时候就转移掉这个 k。所以每次只需要转移新添进候选值的 k,即 k(gi,j,gi+1,j]。因为 g 单调不降,对于一个 jk 的枚举次数即为 (igi,jgi1,j)n,所以这样总复杂度是 O(nV)。输出方案也比较平凡,只需要记录从哪个状态转移过来的即可。

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');}
    }
}
posted @   WrongAnswer_90  阅读(19)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现
· 【杂谈】分布式事务——高大上的无用知识?

This blog has running: 562 days 11 hours 28 minutes 21 seconds

点击右上角即可分享
微信分享提示