AGC024F Simple Subsequence Problem

Simple Subsequence Problem

You are given a set \(S\) of strings consisting of 0 and 1, and an integer \(K\) .

Find the longest string that is a subsequence of \(K\) or more different strings in \(S\) . If there are multiple strings that satisfy this condition, find the lexicographically smallest such string.

Here, \(S\) is given in the format below:

  • The data directly given to you is an integer \(N\) , and \(N+1\) strings \(X_0,X_1,...,X_N\) . For every \(i~(0\leq\ i\leq\ N)\) , the length of \(X_i\) is \(2^i\) .

  • For every pair of two integers \((i,j)~(0\leq\ i\leq\ N,0\leq\ j\leq\ 2^i-1)\) , the \(j\) -th character of \(X_i\) is 1 if and only if the binary representation of \(j\) with \(i\) digits (possibly with leading zeros) belongs to \(S\) . Here, the first and last characters in \(X_i\) are called the \(0\) -th and \((2^i-1)\) -th characters, respectively.

  • \(S\) does not contain a string with length \(N+1\) or more.

\(N\leq 20\).

题解

仓鼠《动态规划选讲》。

因为可能的串\(S\)并不多,所以考虑对于每个串\(S\),计数它是多少个集合串的子序列。

考虑构造出一个类似自动机的东西。

设一个状态\((S, T)\)表示现在有一个串\(S\),它后面需要接一个\(T\)的子序列。

转移就类似在子序列自动机上走的过程,在它后面添加01可以分别走到两个状态,还有一个转移是不加任何字符然后结束。这样状态就构成了一个DAG。

同时这个DAG有个很好的性质,因为是用类似子序列自动机的方法构造出来的,所以两点之间路径唯一。

根据前面的分析,如果\(S\)\(T\)的子序列,那么当且仅当\((\varnothing, T)\)可以走到\((S, \varnothing)\),这里用\(\varnothing\)表示空串。

因为路径唯一,所以直接在DAG上做路径计数即可

因为这个状态满足\(|S| + |T| ≤ N\),所以状态数是\(O(N × 2^N)\)的。转移\(O(1)\),时间复杂度也就是\(O(N × 2^N)\)

这道题的巧妙之处在于,考虑对于每个集合中的串S,让它对它的所有子序列不重不漏产生贡献。暴力只能是对于每个串在子序列自动机上走,但是如果注意到子序列自动机可以压下来,以及后面的一些相关性质,这道题就迎刃而解了。

代码参考:https://www.cnblogs.com/nealchen/p/AGC024F.html

为了描述字符串的长度(针对有前导0的那些串),我们在字符串前加一个1来描述字符串的长度。

#define clz __builtin_clz

char str[(1<<20)+1];
int*F[1<<21],trans[1<<21][2];

int main(){
	int n=read<int>(),K=read<int>();
	F[1]=new int[1<<(n+1)];
	F[1][0]=0;
	for(int i=0;i<=n;++i){
		int p=1<<i;
		scanf("%s",str);
		for(int j=0;j<p;++j) F[1][p+j]=str[j]-'0'; // use a leading 1 to describe the length
		for(int j=0;j<p;++j) trans[p+j][1]=j;
		trans[2*p-1][0]=0; // doesn't exist
		for(int j=1;j<p;++j) trans[2*p-1-j][0]=j^((1<<(31-clz(j)))-1);
	}
	int ans=1;
	for(int i=1;i<=n;++i){
		int p=1<<i;
		for(int j=2*p-1;j>=p;--j){
			F[j]=new int[1<<(n-i+1)];
			memset(F[j],0,sizeof(int)<<(n-i+1));
			for(int k=1;k<1<<(n-i+2);++k) F[j][trans[k][j&1]]+=F[j>>1][k];
			F[j][0]=0;
			if(accumulate(F[j],F[j]+(1<<(n-i+1)),0)>=K) ans=j;
		}
	}
	for(int i=31-clz(ans)-1;i>=0;--i) printf("%d",ans>>i&1);
	puts("");
	return 0;
}

posted on 2020-04-14 08:37  autoint  阅读(172)  评论(0编辑  收藏  举报

导航