【XSY2698】挑选子序列(二分,DLX)
题面
题解
非常 NOI 的一道题(给出很多奇奇怪怪的定义)。
先二分答案为 \(mid\),那么我们只需判定:是否存在子序列 \(seq\) 使得串 \(s1,s2\) 与 \(t\) 的距离不超过 \(mid\),即串 \(s1,s2\) 各位置与 \(t\) 的距离不超过 \(mid\),那么对于串 \(s1,s2\) 的每个位置 \(i\),\(seq\) 中都应该存在一个位置 \(j\) 使得 \(i,j\) 的距离小于等于 \(mid\)。
我们可以先预处理出 \(s1,s2\) 的每个位置 \(i\) 与 \(t\) 的哪些位置 \(j\) 匹配能使得 \(i,j\) 距离小于等于 \(mid\),再转化一下,变为 \(t\) 的每个位置 \(j\) 能与 \(s1,s2\) 的哪些位置 \(i\) 匹配使得 \(i,j\) 距离小于等于 \(mid\)。那么现在问题变为了:能否选出 \(t\) 的不超过 \(m\) 个的位置,使得 \(s1,s2\) 的每个位置都有匹配。
于是这就变成了一个可重复覆盖问题,可以用类似 DLX 的方法解决,需要加大量剪枝优化。比如用状压代替链表加速 DLX 的过程,因为这是可重复覆盖问题,我们不需要维护行的双向链表。
#include<bits/stdc++.h>
#define N 45
#define ll __int128
#define INF 0x7fffffff
using namespace std;
inline int read()
{
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9')
{
if(ch=='-') f=-1;
ch=getchar();
}
while(ch>='0'&&ch<='9')
{
x=(x<<1)+(x<<3)+(ch^'0');
ch=getchar();
}
return x*f;
}
int T,n,m,nn,val[N];
char s[2][N],t[N];
int dis[N][N<<1];
int tot1[N];
ll full,sta[N],ss[N<<1];
vector<int>pos[N<<1];
int now;
inline int get(ll x)
{
int bit=0;
while(x>1) x>>=1,bit++;
return bit;
}
int getp(ll x)
{
int ans=0;
for(int i=1;i<=nn;i++,x>>=1)
if((x&1)&&tot1[i]<tot1[ans]) ans=i;
return ans;
}
int mindep(ll x)
{
int ans=0;
while(x)
{
int p=get(x&-x)+1;
x&=full^ss[p];
ans++;
}
return ans;
}
int aaa[N];
bool dfs(ll x)
{
if(!x) return 1;
if(now==m) return 0;
if(now+mindep(x)>m) return 0;
int p=getp(x);
now++;
for(auto r:pos[p])
if(dfs(x&(full^sta[r]))) return 1;
now--;
return 0;
}
bool check(int mid)
{
for(int i=1;i<=nn;i++) pos[i].clear();
for(int i=1;i<=n;i++)
{
sta[i]=0;
for(int j=1;j<=nn;j++)
{
if(dis[i][j]<=mid)
{
pos[j].push_back(i);
sta[i]|=((ll)1<<(ll)(j-1));
}
}
}
for(int i=1;i<=nn;i++)
{
tot1[i]=pos[i].size();
if(!tot1[i]) return 0;
ss[i]=0;
for(int r:pos[i]) ss[i]|=sta[r];
}
now=0;
return dfs(full);
}
int main()
{
tot1[0]=INF;
T=read();
for(int fuck=1;fuck<=T;fuck++)
{
n=read(),m=read();
nn=n<<1;
full=((ll)1<<(ll)nn)-1;
for(int i=0;i<n;i++) val[i]=read();
scanf("%s%s%s",s[0]+1,s[1]+1,t+1);
int l=0,r=0,ans;
for(int i=1;i<=n;i++)
{
for(int j=0;j<2;j++)
{
for(int k=1;k<=n;k++)
{
dis[i][j*n+k]=abs(t[i]-s[j][k])+val[abs(i-k)];
r=max(r,dis[i][j*n+k]);
}
}
}
while(l<=r)
{
int mid=(l+r)>>1;
if(check(mid)) ans=mid,r=mid-1;
else l=mid+1;
}
printf("Case #%d: %d\n",fuck,ans);
}
return 0;
}
/*
114514
3 2
0 1 2
azz
zaa
zza
7 2
0 1 2 3 4 5 6
elelele
psypsyp
congroo
*/
/*
1
20 5
57 194 48 150 18 110 131 88 94 137 140 159 95 53 43 127 175 7 64 55
lkgjznqsxonxlpbomhix
lcxhxvjckjptarbdgmqj
szkdzeifsuqzafaxpcgk
*/