Codeforces 1550E Stringforces 题解 [ 蓝 ] [ 状压 dp ] [ 二分 ]
Stringforces:不懂了,为啥这么一道纯靠堆 trick 堆出来的唐氏状压能把全机房难住啊,感觉 CF 评的 *2500 都评高了。
思路
首先观察到求的是最大化最小值,所以显然具有单调性,我们考虑二分这个最小值。
那么如何判断每个数是否都能到这个最小值呢?我们首先可以设计一个很暴力的 dp:定义
转移是显然的,但是显然这个 dp 也是会 T 飞的,我们继续观察,这个 dp 式子的值显然只能有两个:
于是现在的 dp 定义就是:
所以我们再定义一个
转移方程为:
那么二分合法的条件就是最终所有字母都满足的情况下的 dp 值
时间复杂度
上面的转移方程用的是填表法,代码里写了刷表法。
代码
#include <bits/stdc++.h>
using namespace std;
const int N=200005;
int n,m,suf[N][20],dp[200005];
char c[N];
void init(int len)
{
memset(suf,0x3f,sizeof(suf));
for(int i=0;i<m;i++)
{
int now=0;
for(int j=n-len+2;j<=n;j++)now+=(!(c[j]=='?'||c[j]-'a'==i));
for(int j=n-len+1;j>=1;j--)
{
now+=(!(c[j]=='?'||c[j]-'a'==i));
suf[j][i]=suf[j+1][i];
if(now==0)suf[j][i]=j+len-1;
now-=(!(c[j+len-1]=='?'||c[j+len-1]-'a'==i));
}
}
}
bool check(int len)
{
if(len>n)return 0;
init(len);
memset(dp,0x3f,sizeof(dp));
dp[0]=0;
for(int i=0;i<(1<<m);i++)
{
int cur=dp[i];
if(cur>=n)continue;
for(int j=0;j<m;j++)
{
if(((i>>j)&1)==0)
{
int v=(i^(1<<j));
dp[v]=min(dp[v],suf[cur+1][j]);
}
}
}
return (dp[(1<<m)-1]<=n);
}
int main()
{
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>n>>m>>c+1;
int l=0,r=200005,mid;
while(l<r)
{
mid=(l+r+1)>>1;
if(check(mid))l=mid;
else r=mid-1;
}
cout<<l;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 微软正式发布.NET 10 Preview 1:开启下一代开发框架新篇章
· 没有源码,如何修改代码逻辑?
· PowerShell开发游戏 · 打蜜蜂
· 在鹅厂做java开发是什么体验
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战