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;
}
/*
*/

浙公网安备 33010602011771号