Atcoder Grand Contest 020 E Encoding Subsets(记忆化搜索+复杂度分析)

Atcoder 题面传送门 & 洛谷题面传送门

首先先考虑如果没有什么子集的限制怎样计算方案数。明显就是一个区间 \(dp\),这个恰好一年前就做过类似的题目了。我们设 \(f_{l,r}\) 为压缩 \([l,r]\) 的方案数,\(g_{l,r}\) 表示压缩 \([l,r]\),并且强制要求 \([l,r]\) 必须用括号括起来的方案数,那么 \(f_{l,r}\) 转移显然可以枚举第一段括号的位置,即 \(f_{l,r}=\sum\limits_{k=l-1}^rg_{l,k}f_{k+1,r}\)\(g_{l,r}\) 转移就枚举循环节长度 \(len\),即 \(g_{l,r}=\sum f_{l,l+len-1}[len\ \text{为}\ s[l...r]\ \text{的循环节}]\)

接下来考虑加上“子集”这个条件之后怎样计算答案,还是设 \(f_{l,r}\) 表示将 \([l,r]\) 所有子集压缩的方案数之和,\(g_{l,r}\) 表示将 \([l,r]\) 所有子集压缩,并且强制要求 \([l,r]\) 必须用括号括起来的方案数。显然 \(f\) 还是一样的转移方式,算 \(g\) 的时候就有些不同了,我们枚举 \(r-l+1\) 的所有约数 \(len\),并考虑 \(s[l...r]\) 中每一段长度为 \(len\) 的字符串,即 \(s[l...l+len-1],s[l+len...l+2len-1]\dots\),我们将这 \(\dfrac{r-l+1}{len}\) 个字符串每一位 AND 起来,然后求它的 \(f\) 值就行了,可以用记忆化搜索实现。

这个算法复杂度看起来不太对。但这只是看起来,事实上这样写即可通过此题。我们粗略估计一下可得 \(T(n)=\sum\limits_{i=1}^n(n-i+1)(\sum\limits_{d\mid i}(i+T(\dfrac{i}{d})))\),稍微打个表即可发现这个 \(T(n)\) 并不会特别大,因此这样写是没问题的。

这题告诉我们看到 \(n\le 100\) 不要习惯性地想 \(n^4\)\(n^3\log n\),有的复杂度奇怪的解法也可以通过这种小数据。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
typedef unsigned int u32;
typedef unsigned long long u64;
namespace fastio{
	#define FILE_SIZE 1<<23
	char rbuf[FILE_SIZE],*p1=rbuf,*p2=rbuf,wbuf[FILE_SIZE],*p3=wbuf;
	inline char getc(){return p1==p2&&(p2=(p1=rbuf)+fread(rbuf,1,FILE_SIZE,stdin),p1==p2)?-1:*p1++;}
	inline void putc(char x){(*p3++=x);}
	template<typename T> void read(T &x){
		x=0;char c=getchar();T neg=0;
		while(!isdigit(c)) neg|=!(c^'-'),c=getchar();
		while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
		if(neg) x=(~x)+1;
	}
	template<typename T> void recursive_print(T x){if(!x) return;recursive_print(x/10);putc(x%10^48);}
	template<typename T> void print(T x){if(!x) putc('0');if(x<0) putc('-'),x=~x+1;recursive_print(x);}
	void print_final(){fwrite(wbuf,1,p3-wbuf,stdout);}
}
const int MOD=998244353;
map<string,int> f,g;
string s;
int calcg(string s);
int calcf(string s){
	if(s==""||s=="0") return 1;
	if(s=="1") return 2;
	if(f.count(s)) return f[s];
	int ret=0;for(int i=1;i<=s.size();i++){
		string s1,s2;
		for(int j=0;j<i;j++) s1+=s[j];
		for(int j=i;j<s.size();j++) s2+=s[j];
		ret=(ret+1ll*calcg(s1)*calcf(s2))%MOD;
	} return f[s]=ret;
}
int calcg(string s){
	if(s==""||s=="0") return 1;
	if(s=="1") return 2;
	if(g.count(s)) return g[s];
	int ret=0;
	for(int i=1;i<s.size();i++){
		if(s.size()%i) continue;string t="";
		for(int j=0;j<i;j++){
			int flg=1;
			for(int k=j;k<s.size();k+=i) flg&=(s[k]-'0');
			t+=(flg^48);
		} ret=(ret+calcf(t))%MOD;
	} return g[s]=ret;
}
int main(){
	cin>>s;printf("%d\n",calcf(s));
	return 0;
}
原文地址:https://www.cnblogs.com/ET2006/p/agc020E.html