【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
*/
posted @ 2022-10-30 10:40  ez_lcw  阅读(39)  评论(0编辑  收藏  举报