国庆集训做题
CSP-S模拟25
t1 : 爱丽丝的数位划分
题意简述 : 将序列A划分为k个不相交连续非空子序列,求最大的总优美度。
优美度指子序列中十进制表示数字不同的个数,一个方案的优美度是所有子序列优美度的和
首先很容易想到 \(n^2m\) 做法,也就是枚举上一个端点进行转移
然后注意到如果多个端点能产生相同的贡献,则端点最后的肯定更优,此时发现贡献的产生只有十种,提前预处理可以做到 \(O(n^2)\) ,多测记得清空
code:
#include<bits/stdc++.h>
using namespace std;
#define int unsigned long long
const int N = 2e3 + 20 ;
int n , T , a[N] , f[N][N] , pos[N][20] , m ,b[N] ;
signed main(){freopen("partition.in","r",stdin);freopen("partition.out","w",stdout);ios :: sync_with_stdio(false) , cin.tie(0) , cout.tie(0) ;cin >> T ; while(T--){cin >> n >> m ;string s ; for(int i = 1 ; i <= n ;i++){cin >> s ; for(int j = 0 ; j < s.size() ; ++j){b[i] |= (1 << (s[j] - '0' + 1));}}for(int i = 1; i <= n ; i++){int l = b[i] ; for(int j = i ; j >= 1 ; j--){l |= b[j];int sum = __builtin_popcount(l);pos[i][sum] = max(pos[i][sum] , j); }}for(int i = 1 ; i <= n ; i++){for(int k = 1 ; k <= m ; k++){for(int j = 0 ; j <= 10 ; j++){if(pos[i][j])f[i][k] = max(f[i][k] , f[pos[i][j] - 1][k - 1] + j) ; }}}cout << f[n][m] << endl ; for(int i = 1 ; i <= n ; i++){for(int j = 1 ; j <= m ; j++){f[i][j] = 0 ; b[i] = 0 ; }for(int j = 1 ; j <= 19 ; j++)pos[i][j] = 0 ; }}return 0;
}
t2 : 爱丽丝的新运算
给出一个由无平方因子数组成的序列,求子序列能构成 : 质因子都出现过偶数次
线性基bitset维护每个序列中质数是否存在的01串,将大于 \(sqrt{n}\) 的数单独处理 , 剩下的插进线性基维护即可
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int mod = 998244353 ;
const int N = 1e6 + 20;
const int M = 1e6 + 20 ;
int T , n , a[N];
int cnt , prime[N] ;
bool not_prime[N] ;
void xxs(){for(int i = 2 ; i <= 1000010 ; i++){if(!not_prime[i]){prime[++cnt] = i ; }for(int j = 1 ; j <= cnt ; j++){if(i * prime[j] > 1000010 )break ; not_prime[i * prime[j]] = 1 ; if(i % prime[j] == 0)break ; }}
}
int ans ;
bitset<180>b[N];
bitset<180>d[M];
void insert(bitset<180>B){int flag = 0 ; for(int i = 170 ; i >= 0 ; --i){if(!(B[i]))continue ; if(d[i].count())B ^= d[i]; else{d[i] |= B ;flag = 1 ; break ; }}if(flag == 0)ans++;
}
int qpow(int a , int b){int res = 1 ; while(b){if(b & 1)res = res * a % mod ; a = a * a % mod ; b >>= 1 ; }return res ;
}
int vis[N] ;
vector<int>G[N];
signed main(){freopen("calc.in","r",stdin);freopen("calc.out","w",stdout);ios :: sync_with_stdio(false) , cin.tie(0) , cout.tie(0) ; xxs() ; cin >> T ;for(int _ = 1 ; _ <= T ; _++){ cin >> n ; for(int i = 1 ; i <= n ; ++i){cin >> a[i] ; }ans = 0 ;sort( a + 1 , a + 1 + n ); for(int i = 1 ; i <= n ; i++){if(a[i] == 1){++ans ;continue ; }int k = a[i] ; for(int j = 1 ; j <= cnt ; j++){if(j == 170)break ; if(k % prime[j] == 0){k /= prime[j] ; b[i][j] = 1 ; }if( k == 1)break ;}if(k == 1){insert(b[i]);}else{if(vis[k]){bitset<180>B ; B = b[i] ^ b[vis[k]];insert(B);}else{vis[k] = i ; }}}for(int i = 1 ; i <= n ; i++){b[i].reset();d[i].reset(); }memset(vis , 0 ,sizeof(vis));}return 0;
}
t3 : 爱丽丝的幻方
给出n * n的一个矩阵,每个矩阵初始状态都是由左到右从上到下编号为1 - n
矩阵支持向右,向下平移操作,对角翻转,旋转,纵向翻转,横向翻转
给出操作后的矩阵,最少需要多少步
观察到1 , 2 , n + 1 三个点相对位置始终不变 , 使用bfs求值
代码太丑了...不贴了
CSP-S 模拟26
t1 : median
求 \(\sum\limits_{i = 1}^n\sum\limits_{j = 1}^n\sum\limits_{k = 1}^n\sum\limits_{l = 1}^n\sum\limits_{m = 1}^n med(A_i,B_j,C_k,D_l,E_m)\) \(mod\) \(998244353\)
其中\(med(a,b,c,d,e)\)为\(a,b,c,d,e\)的中位数。
开题的时候吓坏了
考虑\(A,B,C,D,E\)中哪个数会成为中位数,最后看统计答案发现会重复 , 所以采取将第二元设为字母的方法,他进行排序,就不用容斥了
一个好方法就是输入\(A\)时把数组内元素\(*5\) ,\(B_i * 5 + 1\)以此类推避免排序
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 2e5 + 10 ;
const int mod = 998244353 ;
int n , a[N] , a1[N] , b[N] , b1[N] , c[N] , c1[N] , d[N] , d1[N] , e[N] , e1[N] , ans ;
signed main(){freopen("median.in","r",stdin);freopen("median.out","w",stdout);ios :: sync_with_stdio(false) , cin.tie(0) , cout.tie(0);cin >> n ; for(int i = 1 ; i <= n ; ++i)cin >> a[i] , a1[i] = (a[i] << 3) | 1 ; sort(a + 1,a + 1 + n) , sort(a1 + 1,a1 + 1 + n); for(int i = 1 ; i <= n ; ++i)cin >> b[i] ,b1[i] = (b[i] << 3) | 2 ; sort(b + 1,b + 1 + n) , sort(b1 + 1,b1 + 1 + n);for(int i = 1 ; i <= n ; ++i)cin >> c[i] , c1[i] = (c[i] << 3) | 3; sort(c + 1,c + 1 + n) , sort(c1 + 1,c1 + 1 + n); for(int i = 1 ; i <= n ; ++i)cin >> d[i] , d1[i] = (d[i] << 3) | 4 ; sort(d + 1,d + 1 + n) , sort(d1 + 1,d1 + 1 + n);for(int i = 1 ; i <= n ; ++i)cin >> e[i] , e1[i] = (e[i] << 3) | 5 ; sort(e + 1,e + 1 + n) , sort(e1 + 1,e1 + 1 + n);for(int i = 1 ; i <= 5 ; i++){for(int j = 1 ; j <= n ; j++){if(i == 1){int bx = lower_bound(b1 + 1, b1 + 1 + n , a1[j]) - b1 - 1 , by = n - bx ; int cx = lower_bound(c1 + 1, c1 + 1 + n , a1[j]) - c1 - 1 , cy = n - cx ; int dx = lower_bound(d1 + 1, d1 + 1 + n , a1[j]) - d1 - 1 , dy = n - dx ; int ex = lower_bound(e1 + 1, e1 + 1 + n , a1[j]) - e1 - 1 , ey = n - ex ; // cout << bx << " " <<cx << " " << dx << " " << ex <<endl ; ans += (bx % mod * cx % mod * dy % mod * ey % mod * a[j] % mod) % mod + (bx % mod * dx % mod * cy % mod * ey % mod * a[j] % mod) % mod + (bx % mod * ex % mod * cy % mod * dy % mod * a[j] % mod) % mod + (cx % mod * ex % mod * by % mod * dy % mod * a[j] % mod) % mod + (cx % mod * dx % mod * by % mod * ey % mod * a[j] % mod) % mod + (dx % mod * ex % mod * by % mod * cy % mod * a[j] % mod) % mod ; ans %= mod ; }if(i == 2){int ax = lower_bound(a1 + 1, a1 + 1 + n , b1[j]) - a1 - 1, ay = n - ax ; int cx = lower_bound(c1 + 1, c1 + 1 + n , b1[j]) - c1 - 1, cy = n - cx ; int dx = lower_bound(d1 + 1, d1 + 1 + n , b1[j]) - d1 - 1, dy = n - dx ; int ex = lower_bound(e1 + 1, e1 + 1 + n , b1[j]) - e1 - 1, ey = n - ex ; // cout << ax << " " << cx << " " << dx << " " << ex << endl ; ans += (ax % mod * cx % mod * dy % mod * ey % mod * b[j] % mod) % mod + (ax % mod * dx % mod * cy % mod * ey % mod * b[j] % mod) % mod + (ax % mod * ex % mod * cy % mod * dy % mod * b[j] % mod) % mod + (cx % mod * ex % mod * ay % mod * dy % mod * b[j] % mod) % mod + (cx % mod * dx % mod * ay % mod * ey % mod * b[j] % mod) % mod + (dx % mod * ex % mod * ay % mod * cy % mod * b[j] % mod) % mod ; ans %= mod ; }if(i == 3){int ax = lower_bound(a1 + 1, a1 + 1 + n , c1[j]) - a1 - 1 , ay = n - ax ; int bx = lower_bound(b1 + 1, b1 + 1 + n , c1[j]) - b1 - 1, by = n - bx ; int dx = lower_bound(d1 + 1, d1 + 1 + n , c1[j]) - d1 - 1, dy = n - dx ; int ex = lower_bound(e1 + 1, e1 + 1 + n , c1[j]) - e1 - 1, ey = n - ex ; // cout << ax << " " << bx << " " << dx << " " <<ex << endl ; ans += (ax % mod * bx % mod * dy % mod * ey % mod * c[j] % mod) % mod + (ax % mod * dx % mod * by % mod * ey % mod * c[j] % mod) % mod + (ax % mod * ex % mod * by % mod * dy % mod * c[j] % mod) % mod + (bx % mod * ex % mod * ay % mod * dy % mod * c[j] % mod) % mod + (bx % mod * dx % mod * ay % mod * ey % mod * c[j] % mod) % mod + (dx % mod * ex % mod * ay % mod * by % mod * c[j] % mod) % mod ; ans %= mod ; }if(i == 4){int ax = lower_bound(a1 + 1, a1 + 1 + n , d1[j]) - a1 - 1, ay = n - ax ; int bx = lower_bound(b1 + 1, b1 + 1 + n , d1[j]) - b1 - 1, by = n - bx ; int cx = lower_bound(c1 + 1, c1 + 1 + n , d1[j]) - c1 - 1, cy = n - cx ; int ex = lower_bound(e1 + 1, e1 + 1 + n , d1[j]) - e1 - 1, ey = n - ex ; // cout <<ax << " " << bx << " " << cx << " " << ex << endl ; ans += (ax % mod * bx % mod * cy % mod * ey % mod * d[j] % mod) % mod + (ax % mod * cx % mod * by % mod * ey % mod * d[j] % mod) % mod + (ax % mod * ex % mod * by % mod * cy % mod * d[j] % mod) % mod + (bx % mod * ex % mod * ay % mod * cy % mod * d[j] % mod) % mod + (bx % mod * cx % mod * ay % mod * ey % mod * d[j] % mod) % mod + (cx % mod * ex % mod * ay % mod * by % mod * d[j] % mod) % mod ; ans %= mod ; }if(i == 5){int ax = lower_bound(a1 + 1, a1 + 1 + n , e1[j]) - a1 - 1 , ay = n - ax ; int bx = lower_bound(b1 + 1, b1 + 1 + n , e1[j]) - b1 - 1 , by = n - bx ; int cx = lower_bound(c1 + 1, c1 + 1 + n , e1[j]) - c1 - 1 , cy = n - cx ; int dx = lower_bound(d1 + 1, d1 + 1 + n , e1[j]) - d1 - 1 , dy = n - dx ;// cout << ax << " " << bx << " " << cx << " " << dx << endl; ans += (ax % mod * bx % mod * cy % mod * dy % mod * e[j] % mod) % mod + (ax % mod * cx % mod * by % mod * dy % mod * e[j] % mod) % mod + (ax % mod * dx % mod * by % mod * cy % mod * e[j] % mod) % mod + (bx % mod * dx % mod * ay % mod * cy % mod * e[j] % mod) % mod + (bx % mod * cx % mod * ay % mod * dy % mod * e[j] % mod) % mod + (cx % mod * dx % mod * ay % mod * by % mod * e[j] % mod) % mod ; ans %= mod ; }}}cout << ans << endl ; return 0;
}
t2 : travel
被SKK卡掉了,题意不清,先不管了
t3 : game
有\(n\)堆石子,第\(i\)堆石子有\(a_i\)个,\(A\)和\(B\)玩游戏,\(A\)先手,每次操作可以进行以下操作 :
-
选定一个还有石子的石子堆,记剩下的石子为\(a_i\)。
-
选定一个\(1\) \(≤\) \(x ≤ a_i\) ,将该堆中的 \(x\)个石子移走。
-
选定一个\(0\) \(≤\) \(y\) \(≤\) \(ai\) \(−x\),将该堆中的\(y\)个石子以任意方式分配到剩余的非空石子堆中。
第一个不能操作者输,问\(A\)是否有必胜策略
注意到当能两两分组时,就是必败局面
同时,必胜局面可以转化到必败局面 : 把最大的拿出来给小的补上让他能两两分组,此时必败
所以只需要判断他是否能两两分组
#include<bits/stdc++.h>
using namespace std;
const int N = 2e5 + 20 ;
int T , n , a[N] ;
int main(){freopen("game.in","r",stdin);freopen("game.out","w",stdout);ios :: sync_with_stdio(false) , cin.tie(0) , cout.tie(0) ; cin >> T ; while(T--){cin >> n ; for(int i = 1 ; i <= n ;++i)cin >> a[i] ; sort(a + 1, a + 1 + n);int f = 0 ; for(int i = n ; i >= 1 ; i--){if(a[i] != a[i - 1]){if((n - i + 1) & 1){f = 1 ; }}}if(!f)cout << "No" << endl; else cout << "Yes" << endl ; }return 0;
}
2025多校CSP模拟赛1
t1 : Acowdemia
- 水题
- 直接判断是否有
GC
CG
即可
// ubsan: undefined
// accoders
#include <bits/stdc++.h>
using namespace std;
const int N = 1e3 + 20;
int mp[N][N], bj[N][N], cnt1[N][N];
int dx[4] = { 1, 0, -1, 0 };
int dy[4] = { 0, 1, 0, -1 };
int n, m, ans;
int main() {ios ::sync_with_stdio(false), cin.tie(0), cout.tie(0);cin >> n >> m;string s;for (int i = 1; i <= n; ++i) {cin >> s;for (int j = 0; j < m; ++j) {if (s[j] == 'C')mp[i][j + 1] = 1;if (s[j] == 'G')mp[i][j + 1] = 2;if (s[j] == 'G')++ans;if (s[j] == '.')mp[i][j + 1] = 3;}}for (int i = 1; i <= n; ++i) {for (int j = 1; j <= m; ++j) {if (mp[i][j] == 2) {int cnt = 0;for (int d = 0; d < 4; ++d) {int x = i + dx[d], y = j + dy[d];if (x < 1 or x > n or j < 1 or j > m)continue;if (mp[x][y] == 1)++cnt;}cnt1[i][j] = cnt;int f = 0;if (cnt < 2) {ans--;continue;} else {if (bj[i][j])continue;else {if (cnt == 2) {if (i > 1 and i <= n and j >= 1 and j < m) {if (mp[i - 1][j] == 1 and mp[i][j + 1] == 1 and mp[i - 1][j + 1] == 2 andcnt1[i - 1][j + 1] == 2) {f = 1;bj[i - 1][j + 1] = 1;}}if (i > 1 and i <= n and j > 1 and j <= m) {if (mp[i - 1][j] == 1 and mp[i][j - 1] == 1 and mp[i - 1][j - 1] == 2 andcnt1[i - 1][j - 1] == 2) {f = 1;bj[i - 1][j + 1] = 1;}}if (i >= 1 and i < n and j > 1 and j <= m) {if (mp[i][j - 1] == 1 and mp[i + 1][j] == 1 and mp[i + 1][j - 1] == 2 andcnt1[i + 1][j - 1] == 2) {f = 1;bj[i - 1][j + 1] = 1;}}if (i >= 1 and i < n and j >= 1 and j < m) {if (mp[i + 1][j] == 1 and mp[i][j + 1] == 1 and mp[i + 1][j + 1] == 2 andcnt1[i + 1][j + 1] == 2) {f = 1;bj[i + 1][j + 1] = 1;}}} else {continue;}}}if (f == 1)ans--;}}}cout << ans << endl;return 0;
}
t2 : Transmutation
越做越像网络流,可惜不是
每次二分能制多少Pb
对于不够的情况,递归下去看看后面的能不能填上,如果能,就可以,否则就不行
对于有环的情况 :如果一个点被经过n次那么就一定不行
#include<bits/stdc++.h>
using namespace std;
#define int long long
const int N = 1010 ;
int a[N] , c[N] , n , vis[N];
struct Pb{int x , y;
}b[N];
bool dfs(int k,int tp){if(a[tp] >= k ){a[tp] -= k;return 1;}if(++vis[tp] > n)return 0;k -= a[tp];a[tp] = 0;bool flag = dfs(k , b[tp].x);if(flag == 0)return 0;flag = dfs(k , b[tp].y);if(!flag)return 0;else return 1;
}
bool check(int k){for(int i = 1 ; i <= n ;i++)c[i] = a[i] , vis[i]=0 ; bool ans = dfs(k , 1);for(int i = 1 ; i <= n ;i++)a[i] = c[i] ; return ans;
}
int T , to;
signed main(){cin >> T ; while(T--){cin >> n ; for(int i = 1 ;i <= n;i++){cin >> b[i].x >> b[i].y ; }int sum = 0;for(int i = 1;i <= n;i++){cin >> a[i] , sum += a[i];}int l = a[1] , r = sum + 91 ;int ans = 0;while(l <= r){int mid = (l + r) / 2;if(check(mid)){ans = mid;l = mid + 1;}else{r = mid - 1;}}printf("Case #%lld: %lld\n",++to,ans);for(int i = 1 ; i <= n ; i++){b[i].x = b[i].y = a[i] = 0;} }return 0;
}
t3 : Magneti
这里用了一个trick:预设型DP
首先把磁铁紧贴一起,剩下的插板法处理
按照升序排序,之后就不用处理贡献了
设 \(f_{i,j,k}\) 表示前i个磁铁分j个连通块,用了k个空位的方案数
转移有:
插后面 \(f_{i,j,k}\) \(=\) \(f_{i−1,j−1,k−1}\)
插之前连通块的两端, \(f_{i,j,k}\) \(=\) \(2\) \(×\) \(f_{i−1,j,k}\) \(−len(i)×j\)
并两个连通块 \(f_{i,j,k}\) \(=\) \(f_{i−1,j+1,k}\) \(−2len(i)+1×(j+1)×j\)
答案是 \(\sum\ i≥1\) \(f_{n,1,i}\) \(×(m−i+n)\)