AT3963 [AGC024F] Simple Subsequence Problem 题解

ATcoder
Luogu

Description.

给定 \(m\) 个长度不超过 \(n(n\le 20)\)01 串。
找到字典序最小的字符,使它是至少 \(K\) 个字符串的子序列。

Solution.

首先考虑一个暴力做法,\(dp_{S,T}\) 表示当前答案字符串是 \(T\),待匹配串是 \(S\) 的答案。
每次转移是对 \(T\) 加字符,\(S\) 中找到第一个 \(T\) 插入的字符并把它前面的全都删掉。
发现有个性质,就是 \(|S|+|T|\le n\),通过刚刚转移过程可以得知。
然后空间复杂度就成为了 \(O(2^n\cdot n^2)\)
\(n^2\) 是因为两个字符串长度都不知,需要两维维护。

考虑怎么把 \(O(n^2)\rightarrow O(n)\)
发现在开头插入一个 1,然后每次找到第一个 1 即可。

Coding.

点击查看代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
	x=0;char c=getchar(),f=0;
	for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
	for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
	f?x=-x:x;
}
template<typename T,typename...L>inline void read(T &x,L&...l) {read(x),read(l...);}/*}}}*/
int n,K,nx[1<<21][2],dp[1<<21][21],tn[1<<21];char ch[1<<21];
inline void output(int x,int w) {for(int i=x-1;~i;i--) putchar(((w>>i)&1)^48);putchar('\n');}
int main()
{
	read(n,K);for(int i=0;i<=n;i++)//dp[A][l] -> A=S|T,l 是 T 长度,T 是目标串
		{scanf("%s",ch);for(int j=0;j<(1<<i);j++) dp[j|(1<<i)][i]=ch[j]^48;}
	for(int i=n;~i;i--) for(int j=0;j<(1<<i);j++) for(int k=i;~k;k--) if(dp[(1<<i)|j][k])
	{
		int v=dp[(1<<i)|j][k],S=j>>k,T=j&((1<<k)-1);if(k) dp[(1<<(i-k))|S][0]+=v;
		if(T)
		{
			int u=k-1;while(!((T>>u)&1)) u--;
			dp[(1<<(i-k+u+1))|(S<<(u+1))|(1<<u)|(T&((1<<u)-1))][u]+=v;
		}
		if((~T)&((1<<k)-1))
		{
			int u=k-1;while((T>>u)&1) u--;
			dp[(1<<(i-k+u+1))|(S<<(u+1))|(T&((1<<u)-1))][u]+=v;
		}
	}
	for(int i=n;~i;i--) for(int j=0;j<(1<<i);j++) if(dp[(1<<i)|j][0]>=K) return output(i,j),0;
}
原文地址:https://www.cnblogs.com/pealfrog/p/15407091.html