CSP-S2025题解

T1 社团招新(club)

传送门.
先按最大的选,最多只有一个部门不满足限制,并且可以任意往外调,调整时使减少的值尽量小即可。

T2 道路修复(road)

传送门.
只有最小生成树上的边可能留下,\(2^k\)枚举每个镇子是否开放,边数级别是\(O(kn)\)的,可以先总体排序,枚举时只考虑有用的边即可。复杂度\(O(2^knk\alpha(n))\)

T3 谐音替换(replace)

传送门.
对于一组变换,记\(s_{i,1}=A+B+C\)\(s_{i,2}=A+D+C\)+表示字符串拼接,\(A,C\)可以为空。令字符串\(S\)A{BD{C,上Trie树,\(t_i\)同理,AC自动机跑多模式匹配即可。需要建fail树优化统计答案。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define ll long long
#define df long double
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x
#define i128 __int128

const int N = 1e7 + 5, M = 2e6 + 2, K = 12;
//const int mod = 998244353;
const int mod = 1e9 + 7;
const ll INF = 1e18;
const df eps = 1e-10;

int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }

int n, Q, idx = 0;
int tr[N][28], fail[N], sum[N], tt[N];
vector<int>g[N];
queue<int>q;

void Ins(string s){
	int p = 0, siz = s.size(); s = " " + s;
	for(int i = 1; i <= siz; i++){
		int& y = tr[p][s[i] - 'a'];
		if(! y) y = ++idx;
		p = y;
	}
	sum[p]++;
}

void build(){
	for(int i = 0; i < 27; i++){
		int y = tr[0][i];
		if(! y) continue;
		q.push(y);
		g[0].pb(y);
		fail[y] = 0;
	}
	while(q.size()){
		int x = q.front();
		q.pop();
		for(int i = 0; i < 27; i++){
			int& y = tr[x][i];
			if(y){
				fail[y] = tr[fail[x]][i];
				g[tr[fail[x]][i]].pb(y);
				q.push(y);
			} else y = tr[fail[x]][i];
		}
	}
}

void pre(int x){
	tt[x] += sum[x];
	for(auto y : g[x]){
		tt[y] += tt[x];
		pre(y);
	}
}

int ask(string s){
	int siz = s.size(), p = 0, res = 0; s = " " + s;
	for(int i = 1; i <= siz; i++){
		int y = tr[p][s[i] - 'a'];
		res += tt[y];
		p = tr[p][s[i] - 'a'];
	}
	return res;
}

signed main(){
	n = read(), Q = read();
	for(int t = 1; t <= n + Q; t++){
		string s1, s2; 
		cin >> s1 >> s2;
		int siz = s1.size(); s1 = " " + s1, s2 = " " + s2;
		int l = 1, r = siz;
		while(s1[l] == s2[l] && l <= siz) l++;
		while(s1[r] == s2[r] && r >= 1) r--;
		string s = "";
		for(int i = 1; i < l; i++) s += s1[i];
		s += "{";
		for(int i = l; i <= r; i++) s += s1[i];
		for(int i = l; i <= r; i++) s += s2[i];
		s += "{";
		if(r) for(int i = r + 1; i <= siz; i++) s += s1[i];
		if(t <= n){
			Ins(s); continue;
		} else if(t == n + 1){
			build(); pre(0);
		} 
		if(s1.size() != s2.size()){
			printf("0\n"); continue;
		}
		printf("%d\n", ask(s));
	}
	return 0;
}
/*
*/

T4 员工招聘(employ)

传送门.
只有\(s_i\)1这一天才可能有人留下,记\(k=\sum s_i\),相当于\(n\)个人里选\(k\)个设法满足要求,剩下随意。以下所有关于人的讨论都是\(k\)个人里的。记\(w_i\)表示第\(i\)个人之前一定要走的,即0的数量,\(t_i\)表示决定要走的人数。对于\(c_x\le w_i+t_i\),你就走。否则留下。设\(dp_{i,x,y}\)表示前\(i\)个人,\(x\)人走,\(y\)人留的方案数。记一个\(a_i\)表示\(c_x\le i\)的前缀人数,由于\(w_i+t_i\)不降,当前位置的人走(即选出\(c_x\le w_i+t_i\))是好转移的。如果要留下的话就有点麻烦。可以容斥,钦定留下的人中有一些人不满足留下的条件,即强制令\(c_x\le w_i+t_i\),重设\(dp_{i,x,y}\)的含义,令\(y\)表示留下的人中钦定\(y\)人不满足限制(即限制反向),然后在决策留下时对于是否钦定反向分别转移,统计答案时乘上容斥系数。
感觉是容斥好题,当然也有纯正的DP解法。

点击查看代码
#include<bits/stdc++.h>
using namespace std;
//#define int long long
#define ll long long
#define df long double
#define pp pop_back
#define pb push_back
#define ins insert
#define lowbit(x) x & -x
#define i128 __int128

const int N = 505, M = 2e6 + 2, K = 12;
const int mod = 998244353;
//const int mod = 1e9 + 7;
const ll INF = 1e18;
const df eps = 1e-10;

int read(){int x; scanf("%d", &x); return x; }
ll readll(){ll x; scanf("%lld", &x); return x; }

int n, m;
char s[N];
int a[N], w[N], dp[N][N][N];
ll fac[N];

signed main(){
	fac[0] = 1ll;
	n = read(), m = read();
	for(int i = 1; i <= n; i++) fac[i] = fac[i - 1] * i % mod;
	for(int i = 1; i <= n; i++) cin >> s[i];
	for(int i = 1; i <= n; i++) a[read()]++;
	for(int i = 1; i <= n; i++) a[i] += a[i - 1];
	int tt = 0;
	for(int i = 1; i <= n; i++){
		if(s[i] == '1'){
			tt++;
			w[tt] = i - tt;
		}
	}
	dp[0][0][0] = 1ll;
	for(int i = 1; i <= tt; i++){
		for(int x = 0; x < i; x++){
			for(int y = 0; x + y < i; y++){
				//滚出去 
				dp[i][x + 1][y] += 1ll * dp[i - 1][x][y] * max(a[w[i] + x] - x - y, 0) % mod;
				//留下然后钦定反向 
				dp[i][x][y + 1] += 1ll * dp[i - 1][x][y] * max(a[w[i] + x] - x - y, 0) % mod;
				//留下但是不钦定你 
				dp[i][x][y] += dp[i - 1][x][y]; 
				dp[i][x + 1][y] %= mod, dp[i][x][y + 1] %= mod, dp[i][x][y] %= mod;
			}
		}
	}
	ll res = 0ll;
	for(int x = 0; x <= tt - m; x++){
		for(int y = 0; x + y <= tt; y++){
			if(y & 1) res -= 1ll * dp[tt][x][y] * fac[n - x - y] % mod;
			else res += 1ll * dp[tt][x][y] * fac[n - x - y] % mod;
			(res += mod) %= mod;
		}
	}
	cout << res;
	return 0;
}
/*
*/
posted @ 2025-11-03 15:55  Lordreamland  阅读(48)  评论(0)    收藏  举报