比赛背景: 很抱歉打扰大家,本人于2025年10月30日查出周期性蛋白脂肪缺乏症,学名lnsanus lovis,是一种广泛存在的慢性病,易感人群多为年轻群体,目前暂无有效的治愈方法,只能使用美国公司Kentucky Fired chicken开发的制剂Firend animal protein,经由食道对胃于小肠中受体进行靶向治疗缓解症状,今天该公司降低药物售价的日子,资助50帮我治疗该慢性病。
T1.palindrome
提交链接:题目详情 - palindrome - ZYZOJ
严肃认为数据过于水了。
题面
你有n种字母,其中第n种字母出现次数为a[i]。你想用这些字母拼成若干个串,要求第i种字母恰好
被用了a[i]次,并且要求每个串都是回文串,使得其中最短的串长度最大。
比如四种字符为'a', 'b', 'c', 'd',出现次数分别为{2,3,2,2},那么可能的其中几种方案为
{"acdbbbdca"}, {"abbba", "cddc"}, {"aca", "bbb", "dcd"}, 或{"acdbdca", "bb"}。第一种方案中,最短
的串长度为9,是最优的。
样例
输入:
cpp
4
4
1 1 2 4
3
2 2 2
5
1 1 1 1 1
5
1 1 2 2 3
输出:
cpp
3
6
1
3
分析
这不是烫题吗?我们发现,偶数是一定可行的,然而奇数必须放在中间处理。那么把偶数拆分成长度为2的pair,每个奇数均摊,就可保证最短串最长了。而对于全是偶数,自然是直接相加即可。
正解
cpp
#include <bits/stdc++.h>
using namespace std;
const int N = 50005;
int T, n, a[N];
int main(){
freopen("palindrome.in", "r", stdin);
freopen("palindrome.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> T;
while (T--){
cin >> n;
int cnt_pair = 0;
int cnt_1 = 0;
long long sum = 0;
for (int i = 1; i <= n; i++){
cin >> a[i];
cnt_pair += (a[i] / 2);
if (a[i] % 2 == 1){
cnt_1++;
}
sum += a[i];
}
if (cnt_1 <= 1){
cout << sum << '\n';
continue;
}
cout << 1 + cnt_pair / cnt_1 * 2 << '\n';
}
}
T2.expression
提交窗口:题目详情 - expression - ZYZOJ
题面
你有n个数字a[1],a[2],...,a[n]和n−1个运算符op[1],op[2],...,op[n-1]排成一行,形如a[1] op1 a[2] op2 ... op[n-1] a[n]。其中运算符是+-*中的一个。
每次你会在剩余的数字里面,选出两个相邻的数字,根据它们之间的运算符计算出结果,然后把这两个数和这个运算符替换成算出来的结果。这样进行? − 1 轮之后只剩一个数字。
你想知道对于所有操作的方法,得到的最后的结果的和。两个操作的方法相同,当且仅当每一次操作两种操作方法选的数字位置都一样。
输出答案对10e9 + 7取模的结果,如果余数是负数,把它加上1e9 + 7 后输出。
样例
输入1:
cpp
5
1 4 6 8 3
+*-*
输出1:
cpp
999999689
输入2:
cpp
10
517974382 763433648 185159600 390253537 104798220 734850655 170422025 748232829 820177910 110047331
输出2:
cpp
123105432
分析
显然是区间dp吧......好吧,错误原因是认为这题是神秘思维,将*和-当成分割,然后连subtask都没过/(ㄒoㄒ)/~~
那么,我们好好想一下为啥是区间dp。主要还是因为这题按刚才思路,具备了完整的分段性质(运算符好像本来就是),同时,答案也是左右两端合并而来的。并且,神秘的数据范围给出了,机房大佬击球手说这就是区间dp的上线。好,考虑状态即转移。
设表示
一段计算的期望结果
转移 即为
最后还需乘上
时间复杂度
正解
cpp
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int N = 1005;
int n, dp[N][N];
int f[N]; //阶乘
int c[N][N]; //组合数
char op[N];
signed main(){
freopen("expression.in","r",stdin);
freopen("expression.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n;
f[0] = 1;
for (int i = 1; i <= n; i++){
cin >> dp[i][i];
f[i] = f[i - 1] * i % mod;
c[i][0] = 1;
}
for (int i = 1; i < n; i++)
cin >> op[i];
c[0][0] = 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
for (int l = 2; l <= n; l++){ //len
for (int i = 1; i + l - 1 <= n; i++){
int j = i + l - 1;
for (int k = i; k < j; k++){
int x = c[j - 1 - i][k - i];
if (op[k] == '+')
dp[i][j] = (dp[i][j] + (f[k - i] * dp[k + 1][j] % mod + f[j - k - 1] * dp[i][k] % mod) * x % mod) % mod;
if (op[k] == '-')
dp[i][j] = (dp[i][j] + (f[j - k - 1] * dp[i][k] % mod - f[k - i] * dp[k + 1][j] % mod) * x % mod + mod) % mod;
if (op[k] == '*')
dp[i][j] = (dp[i][j] + dp[i][k] * dp[k + 1][j] % mod * x % mod) % mod;
}
}
}
cout << dp[1][n];
return 0;
}
T3.square
题面
你有个集合{1,2,3,...,n},你想在里面选出一个子集,满足:
Ⅰ.子集非空,并且大小不超过k。
Ⅱ.这些数字的乘积,不包含任何平方因子,也就是不能被任意大于等于2的数字的平方整除。
求选的子集的方案数,答案对1e9 + 7 取模。
样例:
输入1:
cpp
6 4
输出1:
cpp
19
输入2:
cpp
100 20
输出2:
cpp
368723819
分析
传言是神秘的状压dp,然而作为蒟蒻的我并非会......
浅写一下吧......
然后我们发现当时,质数少得可怜,直接暴力。
但题目数据居然是,那么我们引入根号分治的思想,对于
的暴力枚举,剩余的进行状压(即选或不选)。
状压dp记录2~19质因子使用情况,剩下每个分为一组。
用dp[i][j][S]表示前i组数字,选了j个,前面8个质因子使用情况为S的方案数,然后要求每个组只能选一个,这样的方案数,直接计算即可。
然后就没了!?
正解
cpp
#include <bits/stdc++.h>
#define int long long
using namespace std;
const int N = 610, mod = 1e9 + 7;
int n, lim, isp[N], p[N], idx, a[N], m, cnt[N], mk[N], ans, dp[10][10][N], up, f[N], lst[10][10][N];
int c[N][N];
vector<int> v[N];
signed main(){
freopen("square.in", "r", stdin);
freopen("square.out", "w", stdout);
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin >> n >> lim;
f[0] = 1;
for (int i = 1; i <= n; i++)
c[i][0] = 1;
for (int i = 2; i <= n; i++){
if (isp[i])
continue;
p[++idx] = i;
for (int j = 2 * i; j <= n; j+=i)
isp[j] = 1;
}
c[0][0] = 1;
for (int i = 1; i <= n; i++){
for (int j = 1; j <= n; j++){
c[i][j] = (c[i - 1][j] + c[i - 1][j - 1]) % mod;
}
}
for (int i = 1; i <= idx; i++)
if (p[i] * p[i] <= n)
up++;
for (int i = 2; i <= n; i++){
int x = i, f = 0, ff = 0;
for (int j = 1; j <= up; j++){
if (x % p[j])
continue;
f |= (1 << j - 1);
x /= p[j];
if (x % p[j])
continue;
ff = 1;
break;
}
if (ff)
continue;
a[++m] = i;
cnt[m] = f;
mk[m] = (x != 1);
if (mk[m])
v[x].push_back(m);
}
dp[0][0][0] = 1;
for (int i = 1; i <= m; i++){
if ((!cnt[i]) || mk[i])
continue;
for (int j = 0; j <= up; j++){
for (int k = up; k >= 1; k--){
for (int l = 0; l < (1 << up); l++){
if ((l | cnt[i]) != l)
continue;
if (j >= mk[i] && k >= 1)
dp[j][k][l] = (dp[j][k][l] + dp[j - mk[i]][k - 1][l ^ cnt[i]]) % mod;
}
}
}
}
for (int r = p[up] + 1; r <= n; r++){
for (int j = 0; j <= up; j++)
for (int k = 0; k <= up; k++)
for (int l = 0; l < (1 << up); l++)
lst[j][k][l] = dp[j][k][l];
for (int i : v[r]){
if (!cnt[i])
continue;;
for (int j = 0; j <= up; j++){
for (int k = up; k >= 1; k--){
for (int l = 0; l < (1 << up); l++){
if ((l | cnt[i]) != l)
continue;
if (j >= mk[i] && k >= 1)
dp[j][k][l] = (dp[j][k][l] + lst[j - mk[i]][k - 1][l ^ cnt[i]]) % mod;
}
}
}
}
}
for (int i = 1; i <= lim; i++){
for (int j = 0; j <= up; j++){
for (int k = 0; k <= min(lim, up); k++){
for (int l = 0; l < (1 << up); l++){
ans = (ans + dp[j][k][l] * c[idx - up - j + 1][i - k] % mod) % mod;
}
}
}
}
cout << ans;
return 0;
}
T4.color
提交链接:题目详情 - color - ZYZOJ
题面
你有一个n*m的棋盘,开始有些格子被染上了红色或者蓝色,有些格子没有颜色。你想对所有未染色的格子染上红色或者蓝色,使得棋盘中每个长宽都为偶数的矩形里面红色蓝色出现个数相同。
求染色的方案数,答案对1e9+7取模。
样例
输入
cpp
3 3
???
?R?
???
输出
cpp
7
大家猜我会吗?
直接上代码!
分析
首先一个条件是每个2×2的子矩形,里面的红色和蓝色个数是一样多的。如果棋盘中有两个相邻的元素同色,那么有如下
????BB????
????RR????
????BB????
????RR????
...
同时,对于每一列也一定是RB交替的,这样也一定满足条件。如果没有相邻的同色,那么整个矩形是红蓝相间的,也满足条件。换句话来说,把所有(i,j)满足i+j是偶数的位置,颜色红蓝反转,那么一定是所有行都同色或者所有列都同色。于是可以计算所有行同色所有列同色方案,减去两个都同色的情况即可.
正解
cpp
#include<bits/stdc++.h>
#define int long long
#define N 1005
#define mod 1000000007
using namespace std;
int T=1,n,m,res;
char a[N][N],b[N][N];
int check(){
int sum=1;
for(int i=1;i<=n;i++){
bool f1=1,f2=1;
for(int j=1;j<=m;j++){
if(j&1)b[i][j]='R';
else b[i][j]='B';
if(a[i][j]!='?'&&a[i][j]!=b[i][j]){
f1=0;
break;
}
}
for(int j=1;j<=m;j++){
if(j&1)b[i][j]='B';
else b[i][j]='R';
if(a[i][j]!='?'&&a[i][j]!=b[i][j]){
f2=0;
break;
}
}
if(!f1&&!f2)return 0;
if(f1&&f2)(sum*=2)%=mod;
}
return sum;
}
void solve(int cs){
cin>>n>>m;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
cin>>a[i][j];
}
}
res=check();
bool flag=1;
for(int i=1;i<=n;i++){
for(int j=1;j<=m;j++){
if(a[i][j]=='?')continue;
char c;
if(a[i][j]=='R'){
if(i&1)c='R';
else c='B';
}
else{
if(i&1)c='B';
else c='R';
}
if(a[1][j]!='?'&&c!=a[1][j]){
flag=0;
break;
}
a[1][j]=c;
}
}
if(flag){
int tot=1;
for(int j=1;j<=m;j++){
if(a[1][j]=='?')(tot*=2)%=mod;
}
bool f1=1,f2=1;
for(int j=1;j<=m;j++){
if(a[1][j]=='?')continue;
if(j&1){
if(a[1][j]!='R'){
f1=0;
break;
}
}
else{
if(a[1][j]!='B'){
f1=0;
break;
}
}
}
for(int j=1;j<=m;j++){
if(a[1][j]=='?')continue;
if(j&1){
if(a[1][j]!='B'){
f2=0;
break;
}
}
else{
if(a[1][j]!='R'){
f2=0;
break;
}
}
}
if(f1)(tot+=mod-1)%=mod;
if(f2)(tot+=mod-1)%=mod;
(res+=tot)%=mod;
}
cout<<res<<'\n';
}
signed main(){
freopen("color.in","r",stdin);
freopen("color.out","w",stdout);
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);
// cin>>T;
for(int cs=1;cs<=T;cs++){
solve(cs);
}
return 0;
}
he的机房巨佬aoao的!