CSP-S 2025 题解

CSP-S 2025 题解

club

先贪心,每个数都选取 \(\max\)。考虑调整法解决 \(n/2\) 的限制,找到超过 \(n/2\) 的集合,按照次大值减去最大值的大小,从小到大把它们调整成次大值即可。复杂度 \(O(n\log n)\)(需要排序)。

road

先把初始 \(m\) 条边做最小生成树这样就一共 \(O(nk)\) 条边。暴力地想法枚举哪些 EX 点被选择,然后每次取出 \(O(nk)\) 条边然后跑最小生成树,可以做到 \(O(2^knk(\log k+\alpha(n)))\)

Trick:求一个图的 MST,可以求若干边集的导出子图的 MST,要求其中边集的并集为全集,对这些 MST 含有的边再做一次 MST 即可。

证明:假设最终 MST 中有一条边不是任何边集导出子图的 MST 中的边,那么找到这条边 \((u,v)\) 所属于的一个边集,那么边集的 MST 有一条 \(u\)\(v\) 的链不经过 \((u,v)\),那么在最终 MST 中删掉 \((u,v)\) 并连上链上一条边使得最终 MST 中 \(u,v\) 连通,这样操作一定不劣,不断调整一定能找到满足条件的最终 MST。

根据以上 Trick,我们可以存下来每种 Ex 点选择方案最后保留下来的 \(O(n+k)\) 条边,然后计算新的一种方案时,我们从原来的某个方案再添加 \(n\) 条边跑一遍 MST 即可,可以用归并排序把 $\log $ 去掉,复杂度 \(O(2^kn\alpha (n))\)(关于 \(k\) 的多项式看做常数)。

replace

\(s_1,s_2\) 找到最长相等的前后缀,\(t_1,t_2\) 也这样。那么发现 \(s_1,s_2\) 是一组合法替换只可能是它们中间不相等的串分别匹配 \(t_1,t_2\) 中间不相等的串。

考虑转化为匹配问题,假设 \(s_1=ABC,s_2=ADC\),那么可以将它们合成 \(A?BD?C\),其中 \(?\) 是特殊字符;对 \(t_1,t_2\) 也这样。那么用 AC 自动机做多模式匹配即可,复杂度 \(O(L\times \sum)\),其中 \(\sum\) 表示字符集大小。

据说还有线性的哈希做法。

employ

考虑指定哪些人被录用,那么记 \(s_i\) 表示前 \(i\) 个人未被录用的个数,则指定被录用的人要满足 \(c_{p_i}>s_i\),否则要满足 \(c_{p_i}\le s_i\)

考虑把 \(c_{p_i}>s\) 变成「任意选 \(-(c_{p_i}\le s)\)」,同时 \(str_i=0\) 的位置也是任意选。

则设 DP \(f_{i,j,k}\) 表示考虑完前 \(i\) 个人,\(s_i=j\),且任意选了 \(k\) 个人。上述 「任意选 \(-(c_{p_i}\le s)\)」其实就相当于有两种方案,一种任意选,另一种以 \(-1\) 的贡献选 \(c_{p_i}\le s\)。最后方案数乘上 \(k!\) 即可。

具体地,有转移:

  • \(str_i=0\) 时,\(f_{i,j,k}\to f_{i+1,j+1,k+1}\)
  • \(str_i=1\) 时(其中 \(C\) 为前面小于等于 \(s_i\) 的个数,容易计算):
    • \(f_{i,j,k}\times C\to f_{i+1,j+1,k}\)
    • \(f_{i,j,k}\times (-1)\to f_{i+1,j,k}\)
    • \(f_{i,j,k}\to f_{i+1,j,k+1}\)

代码

replace

const int N=2e5+5,ln=5e6+5+2*N;
char s1[ln],s2[ln];
int tr[ln][27],tot,s[ln],fail[ln];
vi G[ln];
void insert(int &x,int y) {
	if(!tr[x][y]) tr[x][y]=++tot;
	x=tr[x][y];
}
void dfs(int x) {
	for(int v:G[x]) s[v]+=s[x],dfs(v); 
}
int n,Q;
void Main() {
	cin>>n>>Q;
	fo(i,1,n) {
		scanf("%s",s1+1);
		scanf("%s",s2+1);
		int len=strlen(s1+1);
		int pre=0,suf=len+1;
		while(pre<len&&s1[pre+1]==s2[pre+1]) ++pre;
		while(suf>1&&s1[suf-1]==s2[suf-1]) --suf;
		int x=0;
		fo(i,1,pre) insert(x,s1[i]-'a');
		// if(pre!=len) {
			insert(x,26);
			fo(i,pre+1,suf-1) insert(x,s1[i]-'a');
			fo(i,pre+1,suf-1) insert(x,s2[i]-'a');
			insert(x,26);
			fo(i,suf,len) insert(x,s2[i]-'a');
		// }
		s[x]++;
	}  
	queue<int> q;
	fu(i,0,27) if(tr[0][i]) q.push(tr[0][i]);
	while(Size(q)) {
		int u=q.front(); q.pop();
		fu(i,0,27) if(tr[u][i]) 
			fail[tr[u][i]]=tr[fail[u]][i],q.push(tr[u][i]);
			else 
				tr[u][i]=tr[fail[u]][i];
	}
	fo(i,1,tot) G[fail[i]].eb(i);
	dfs(0);
	while(Q--) {
		scanf("%s",s1+1);
		int len=strlen(s1+1);
		scanf("%s",s2+1);
		if(strlen(s2+1)!=len) {printf("0\n"); continue;}
		int pre=0,suf=len+1;
		while(pre<len&&s1[pre+1]==s2[pre+1]) ++pre;
		while(suf>1&&s1[suf-1]==s2[suf-1]) --suf;
		ll ans=0;
		int x=0;
		fo(i,1,pre) x=tr[x][s1[i]-'a'],ans+=s[x];
		// if(pre!=len) {
			x=tr[x][26],ans+=s[x];
			fo(i,pre+1,suf-1) x=tr[x][s1[i]-'a'],ans+=s[x];
			fo(i,pre+1,suf-1) x=tr[x][s2[i]-'a'],ans+=s[x];
			x=tr[x][26],ans+=s[x];
			fo(i,suf,len) x=tr[x][s2[i]-'a'],ans+=s[x];
		// }
		printf("%lld\n",ans);
	}
}

employ

const int N=505,mod=998244353;
int mul(int x,int y) {return (ull)x*y%mod;}
void inc(int &x,int y) {x+=y; if(x>=mod) x-=mod;}
int n,m;
char str[N];
int fac[N];
int f[N][N],g[N][N],c[N],s[N];
void Main() {
	fac[0]=1;
	fu(i,1,N) fac[i]=mul(fac[i-1],i);
	cin>>n>>m;
	fo(i,1,n) cin>>str[i];
	fo(i,1,n) cin>>c[i],s[c[i]]++;
	fo(i,1,n) s[i]+=s[i-1];
	f[0][0]=1;
	fo(i,1,n) {
		memset(g,0,sizeof g);
		fo(j,0,i-1) {
			fo(k,0,i-1) {
				if(str[i]=='0') {
					inc(g[j+1][k+1],f[j][k]);
				}
				else {
					int res=s[j]-(i-1-k);
					inc(g[j+1][k],mul(f[j][k],res));
					inc(g[j][k],mod-mul(f[j][k],res));
					inc(g[j][k+1],f[j][k]);
				}
			}
		}
		memcpy(f,g,sizeof f);
	} 
	int ans=0;
	fo(j,0,n-m) fo(k,0,n) inc(ans,mul(f[j][k],fac[k]));
	cout<<ans<<'\n';
}
posted @ 2025-11-03 22:19  dengchengyu  阅读(17)  评论(0)    收藏  举报