Codeforces Round #791 (Div. 2) E, F 题题解 (2400,2600)

\(\mathbb{E}\)

给定一个长度为 \(n\) 的字符串 \(s\),由前 \(k\) 个小写字母和 \(\texttt{?}\) 组成。
\(q\) 次询问,给出一个前 \(k\) 个小写字母的子集 \(t\),问将 \(s\) 中的所有 \(\texttt{?}\) 替换成 \(t\) 中的任意一个字符的所有方案,\(s\) 包含的所有可以本质相同的回文字串的个数的和,答案对 \(998244353\) 取模。
\(1\le n\le 10^3,1\le q\le 2\cdot 10^5,1\le k\le 17\)


随便做。

考虑回文串中的两个对称位置 \(i,j\),有四种情况:

  • \(s_i=s_j=\texttt{?}\):贡献为 \(|t|\)
  • \(s_i=\texttt{?},s_j\neq \texttt{?}\):贡献为 \([s_j\in t]\)
  • \(s_i\neq \texttt{?},s_j=\texttt{?}\):贡献为 \([s_i\in t]\)
  • \(s_i\neq \texttt{?},s_j\neq \texttt{?}\):贡献为 \([s_i=s_j]\)

那么区间 \(\text{dp}\) 出每个区间的贡献,每个贡献形如 \(|t|^{p}\times [S\in t]\),于是高维前缀和即可。

总时间复杂度 \(O(n^2+2^{17}\times 17^{2})\)

\(\color{blue}{\text{code}}\)

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=1005,mod=998244353;
int n,S,q,sum[N],P[18][N],pw[N][N],zero[N][N],sta[N][N],f[1<<17][18];
char s[N],t[N];
int main(){
	scanf("%d%s",&n,s+1);
	for(int i=1;i<=n;++i)sum[i]=sum[i-1]+(s[i]=='?');
	for(int L=1;L<=17;++L){
		P[L][0]=1;
		for(int i=1;i<=n;++i)
			P[L][i]=(ll)P[L][i-1]*L%mod;
	}
	for(int t=1;t<=n;++t)
		for(int i=1;i<=n-t+1;++i){
			int j=i+t-1;
			zero[i][j]=zero[i+1][j-1];
			pw[i][j]=pw[i+1][j-1];
			sta[i][j]=sta[i+1][j-1];
			if(s[i]=='?'&&s[j]=='?')++pw[i][j];
			else if(s[i]!='?'&&s[j]!='?')zero[i][j]|=s[i]!=s[j];
			else sta[i][j]|=(s[i]!='?'?(1<<s[i]-'a'):(1<<s[j]-'a'));
			if(!zero[i][j])
				for(int L=1;L<=17;++L)
					(f[sta[i][j]][L]+=P[L][pw[i][j]+sum[n]-(sum[j]-sum[i-1])])%=mod;
		}
	for(int i=0;i<17;++i)
		for(int j=0;j<1<<17;++j)if(j>>i&1)
			for(int k=1;k<=17;++k)(f[j][k]+=f[j^(1<<i)][k])%=mod;
	for(scanf("%d",&q);q--;){
		scanf("%s",t+1),S=0;
		for(int i=1;i<=strlen(t+1);++i)S|=1<<t[i]-'a';
		printf("%d\n",f[S][strlen(t+1)]);
	}
	return 0;
}

\(\mathbb{F}\)

给出正整数 \(n\),所有不足 \(n\) 位(十进制)的数用前导零补充。
给出 \(m\)无序数对 \((u_i,v_i)\),若一个数字的相邻两位数 \(x,y\) 满足 \((x,y)\) 存在于这 \(m\) 组数对中,则可以交换 \(x,y\) 的位置。若 \(A\) 可以通过若干次(包含零次)交换得到 \(B\),则认为 \(A\)\(B\) 是等价的。
求出最大整数 \(k\),使得存在一组非负整数 \(x_1,x_2,\ldots,x_k(0\leq x_i<10^n)\) 满足对于任意 \(1\leq i<j\leq k\)\(x_i\)\(x_j\) 不等价。


更简单,不知道为何能放到 \(\text{F}\) 的位置,标签竟有 \(2600\)

考虑 \(10^n\) 个数中形成若干等价类,要求的就是等价类的数量。对于一个等价类,考虑用其字典序最小的元素描述它。

如何保证字典序最小呢?字典序最小等价于不能再通过操作使得字典序更小,形式化地,设字典序最小的数为 \(\overline{d_1d_2\ldots d_n}_{10}\),不能找出 \(d_j\) 使得 \(d_j\)\(d_i,d_{i+1},\ldots, d_{j-1}\) 都可以交换并且 \(d_j<d_i\)

于是可以从低位到高位状压 \(\text{dp}\),同时维护状态 \(s\),表示能从后面移动到当前位置的数字集合,每次重新求 \(s\) 时间复杂度不对,那么进行预处理,\(g_{s,i}\) 表示现在的集合为 \(s\),在前面加入 \(i\) 后新的 \(s\),然后进行 \(\text{dp}\),转移时不能使得后面能交换到当前位置的数字比当前填的数字小。

总时间复杂度 \(O(n\times 2^{10}\times 10)\)

\(\color{blue}{\text{code}}\)

#include<bits/stdc++.h>
using namespace std;
const int mod=998244353;
int n,m,ans,f[50005][1024],g[1024][10],G[10][10];
int main(){
	scanf("%d%d",&n,&m),f[0][0]=1;
	for(int i=1,x,y;i<=m;++i)scanf("%d%d",&x,&y),G[x][y]=G[y][x]=1;
	for(int s=0;s<1024;++s)
		for(int i=0;i<=9;++i){
			g[s][i]=1<<i;
			for(int t=0;t<=9;++t)
				if(G[i][t]&&(s>>t&1))
					if(i<t){g[s][i]=-1;break;}
					else g[s][i]|=1<<t;
		}
	for(int i=0;i<n;++i)
		for(int s=0;s<1024;++s)if(f[i][s])
			for(int j=0;j<10;++j)if(~g[s][j])
				(f[i+1][g[s][j]]+=f[i][s])%=mod;
	for(int s=0;s<1024;++s)(ans+=f[n][s])%=mod;
	return printf("%d\n",ans),0;
}
posted @ 2022-06-12 18:41  Samsara-soul  阅读(54)  评论(0编辑  收藏  举报