[HAOI2016]字符合并
一个长为 的 01 串 ,可以选长为 的连续段 替换为 ,得到 价值,其中 表示 构成的二进制数。例如
10011
将中间三个字符替换为1
,会得到新字符串111
,而后续的操作均在新字符串上进行。
受到启发
- 看到 想到状态压缩。
- 看到 想到区间 dp。
观察性质
- 将一个区间从始态到终态的过程本质是将区间划分成 段,每一段中的数合并成一个数。
- 由于 ,对于一固定长度区间,它最后的长度是确定的,因一次操作将区间长削减 。为
y=len%(k-1)==0?(k-1):len%(k-1)
。
题解
表示区间 的终态为 的最大价值。 是一个 y
位二进制数,y
的含义见上。
枚举 的最后一位所代表的区间后缀的起始点 ,。
。
观察到有效地 只可能是 ,这样枚举可将常数 。
。区间 dp 常数小,可通过。
一些注意事项:
- LL
- 枚举 时,如果
y==1
,发现没法转移。考虑倒数第二次转移的末态作为 来。即y+=(k-1)
。但储存f[l,r,s]
时的 还是得按最后一次的 来表示。
复制#include <bits/stdc++.h> #define int long long using namespace std; const int N=305; int n,k,ans,a[N],c[260],v[260],f[N][N][260]; signed main(){ cin>>n>>k; for(int i=1;i<=n;i++)cin>>a[i]; for(int i=0;i<(1<<k);i++)cin>>c[i]>>v[i]; memset(f,-0x3f,sizeof(f)); for(int i=1;i<=n;i++)f[i][i][a[i]]=0; for(int l=1;l+k-1<=n;l++){ int x=0; for(int i=l;i<=l+k-1;i++)x=(x<<1)+a[i]; f[l][l+k-1][c[x]]=v[x]; } for(int len=2;len<=n;len++){ for(int l=1,r=len;r<=n;l++,r++){ int x=len%(k-1);if(!x)x+=k-1;if(x<=1)x+=k-1; for(int s=0;s<(1<<x);s++){ int _s=x==k?c[s]:s; int tmp=f[0][0][0]; for(int i=r;i>l;i-=k-1) if(f[l][i-1][s>>1]!=f[0][0][0]&&f[i][r][s&1]!=f[0][0][0]) tmp=max(tmp,f[l][i-1][s>>1]+f[i][r][s&1]); if(x==k&&tmp!=f[0][0][0])tmp+=v[s]; f[l][r][_s]=max(f[l][r][_s],tmp); if(len==n)ans=max(ans,f[l][r][_s]); } } } cout<<ans; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通