zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    LG2664 树上游戏
    「NOI2007」 货币兑换
    「NOI2012」骑行川藏
    LG4721 【模板】分治 FFT
    LG4783 【模板】矩阵求逆
    test20181019 B君的第二题
    LOJ129 Lyndon 分解
    「NOI2017」泳池
    LG4723 【模板】常系数线性递推
    「AHOI / HNOI2017」礼物
  • 原文地址:https://www.cnblogs.com/ET2006/p/agc020E.html
Copyright © 2011-2022 走看看