A - Q老师与石头剪刀布(必做)
每一个大人曾经都是一个小孩,Q老师 也一样。
为了回忆童年,Q老师 和 Monika 玩起了石头剪刀布的游戏,游戏一共 n 轮。无所不知的 Q老师 知道每一轮 Monika 的出招,然而作为限制, Q老师 在这 n 轮游戏中必须恰好出 a 次石头,b 次布和 c 次剪刀。
如果 Q老师 赢了 Monika n/2(上取整) 次,那么 Q老师就赢得了这场游戏,否则 Q老师 就输啦!
Q老师非常想赢,他想知道能否可以赢得这场游戏,如果可以的话,Q老师希望你能告诉他一种可以赢的出招顺序,任意一种都可以。
Input
第一行一个整数 t(1 ≤ t ≤ 100)表示测试数据组数。然后接下来的 t 组数据,每一组都有三个整数:
- 第一行一个整数 n(1 ≤ n ≤ 100)
- 第二行包含三个整数 a, b, c(0 ≤ a, b, c ≤ n)。保证 a+b+c=n
- 第三行包含一个长度为 n 的字符串 s,字符串 s 由且仅由 ‘R’, ‘P’, ‘S’ 这三个字母组成。第 i 个字母 s[i] 表示Monika 在第 i 轮的出招。字母 ‘R’ 表示石头,字母 ‘P’ 表示布,字母 ‘S’ 表示剪刀
Output
对于每组数据:
- 如果 Q老师 不能赢,则在第一行输出 “NO”(不含引号)
- 否则在第一行输出 “YES”(不含引号),在第二行输出 Q老师 的出招序列 t。要求 t 的长度为 n 且仅由 ‘R’, ‘P’, 'S’这三个字母构成。t 中需要正好包含 a 个 ‘R’,b 个 ‘P’ 和 c 个 ‘S’
“YES”/"NO"是大小写不敏感的,但是 ‘R’, ‘P’, ‘S’ 是大小写敏感的。
Sample
Input
2
3
1 1 1
RPS
3
3 0 0
RPS
Output
YES
PSR
NO
思路
思路不是很难,既然已经知道了对方的出招顺序,那么就对应出招(如果次数够的话),如果可以赢的话要记录出招顺序,因此就使用一个数组来记录出招顺序,如果这一次没法出招,先在数组中记录了为0
在赢的情况下,如果输出出招顺序时遇到0,就从剩下的招中随便挑一个,反正这一次都赢不了,
在判断是否赢时,题目要求赢的次数是总次数的一半上取整,要注意这时应该除2.0来判断,否则就是下取整了
代码
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <cmath>
using namespace std;
int t,n,a,b,c,num;
string str,ans;
int main(){
scanf("%d",&t);
while(t--){
num=0;str.clear();ans.clear();
scanf("%d%d%d%d",&n,&a,&b,&c);
// scanf("%s",str);
cin>>str;
ans.resize(str.size()+10);
for(int i=0;i<n;i++){
if(str[i]=='R' && b>0){
ans[i]='P';b--;num++;
}
else if(str[i]=='S' && a>0){
ans[i]='R';a--;num++;
}
else if(str[i]=='P' && c>0){
ans[i]='S';c--;num++;
}
else ans[i]='0';
}
if(num<n/2.0) {
printf("NO\n");continue;
}
else {
printf("YES\n");
for(int i=0;i<n;i++){
if(ans[i]!='0')
printf("%c",ans[i]);
else{
if(a>0) {printf("R");a--;}
else if(b>0) {printf("P");b--;}
else if(c>0) {printf("S");c--;}
}
}
printf("\n");
}
}
return 0;
}
B - Q老师与十字叉(必做)
Q老师 得到一张 n 行 m 列的网格图,上面每一个格子要么是白色的要么是黑色的。
Q老师认为失去了 十字叉 的网格图莫得灵魂. 一个十字叉可以用一个数对 x 和 y 来表示, 其中 1 ≤ x ≤ n 并且 1 ≤ y ≤ m, 满足在第 x 行中的所有格子以及在第 y 列的 所有格子都是黑色的
例如下面这5个网格图里都包含十字叉
第四个图有四个十字叉,分别在 (1, 3), (1, 5), (3, 3) 和 (3, 5).
下面的图里没有十字叉
Q老师 得到了一桶黑颜料,他想为这个网格图注入灵魂。 Q老师每分钟可以选择一个白色的格子并且把它涂黑。现在他想知道要完成这个工作,最少需要几分钟?
Input
第一行包含一个整数 q (1 ≤ q ≤ 5 * 10^4) — 表示测试组数
对于每组数据:
第一行有两个整数 n 和 m (1 ≤ n, m ≤ 5 * 10^4, n * m ≤ 4 * 10^5) — 表示网格图的行数和列数
接下来的 n 行中每一行包含 m 个字符 — ‘.’ 表示这个格子是白色的, ‘*’ 表示这个格子是黑色的
保证 q 组数据中 n 的总和不超过 5 * 10^4, n*m 的总和不超过 4 * 10^5
Output
答案输出 q 行, 第 i 行包含一个整数 — 表示第 i 组数据的答案
Sample
Input
9
5 5
..*..
..*..
*****
..*..
..*..
3 4
****
.*..
.*..
4 3
***
*..
*..
*..
5 5
*****
*.*.*
*****
..*.*
..***
1 4
****
5 5
.....
..*..
.***.
..*..
.....
5 3
...
.*.
.*.
***
.*.
3 3
.*.
*.*
.*.
4 4
*.**
....
*.**
*.**
Output
0
0
0
0
0
4
1
1
2
思路
一开始的想法当然是既然是十字形的,那么就找行与列都需要涂色的最小的
int nmax=0,mmax=0,nindex=0,mindex=0;
for(int i=0;i<n;i++){
if(row[i]>nmax)
nmax=row[i],nindex=i;
}
for(int j=0;j<m;j++){
if(column[j]>mmax)
mmax=column[j],mindex=j;
}
想法是对的,但是考虑不全面,比如
3行4列:有5个
∗
*
∗,需要涂色7个
3行5列,有5个
∗
*
∗,需要涂色8个
因此需要遍历整个二维数组来找到最小的
需要注意的有
- 网格如果使用二维数组记录,数组会很大,编译不通过,静态区最大能开2G的数组,但是题目中有m*n的范围,可以使用一维数组来模拟二维数组
- 一开始为了方便,直接将row 与column这两个数组都开成4e5的了,高了一个数量级,在一些情况下,使用memset对性能不会有太大的影响,但是题中组数很多,5e4,这时放在循环中的memset就会超时了,所以数组开的合适就好
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int N=4e5+5;
const int MN=5e4+5;
int q,ans,m,n;
char str[N];
int row[MN],column[MN];
void show(){
cout<<"show:"<<endl;
int cnt=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
cout<<str[cnt];cnt++;
}
cout<<endl;
}
cout<<endl;
}
int main(){
scanf("%d",&q);
char c;
while(q--){
// memset(str,0,sizeof str);
memset(row,0,sizeof row);
memset(column,0,sizeof column);
// ans=0;
scanf("%d %d",&n,&m);
int cnt=0;getchar();
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
// cin>>str[i*m+j];
scanf("%c",str+cnt);
if(str[cnt]=='.') row[i]++,column[j]++;cnt++;
// str[i*m+j]=c;
// str[cnt]=c;cnt++;
}
getchar();
}
// show();
ans=1e8;
int temp=0;cnt=0;
for(int i=0;i<n;i++){
for(int j=0;j<m;j++){
temp=row[i]+column[j];
if(str[cnt]=='.') temp--;
ans=min(ans,temp);
cnt++;
if(ans==0) break;
}
if(ans==0) break;
}
printf("%d\n",ans);
// cout<<ans<<endl;
// int nmax=0,mmax=0,nindex=0,mindex=0;
// for(int i=0;i<n;i++){
// if(row[i]>nmax)
// nmax=row[i],nindex=i;
// }
// for(int j=0;j<m;j++){
// if(column[j]>mmax)
// mmax=column[j],mindex=j;
// }
//
// if(str[nindex*m+mindex]=='*')
// ans=(m+n-1) - (nmax+mmax-1);
// else
// ans=(m+n-1) - (nmax+mmax);
// cout<<ans<<endl;
}
}
C - Q老师的考验(必做)
Q老师 对数列有一种非同一般的热爱,尤其是优美的斐波那契数列。
这一天,Q老师 为了增强大家对于斐波那契数列的理解,决定在斐波那契的基础上创建一个新的数列 f(x) 来考一考大家。数列 f(x) 定义如下:
当 x < 10 时,f(x) = x;
当 x ≥ 10 时,f(x) = a0 * f(x-1) + a1 * f(x-2) + a2 * f(x-3) + …… + a9 * f(x-10),ai 只能为 0 或 1。
Q老师 将给定 a0~a9,以及两个正整数 k m,询问 f(k) % m 的数值大小。
聪明的你能通过 Q老师 的考验吗?
Input
输出文件包含多组测试用例,每组测试用例格式如下:
第一行给定两个正整数 k m。(k < 2e9, m < 1e5)
第二行给定十个整数,分别表示 a0~a9。
Output
对于每一组测试用例输出一行,表示 f(k) % m 的数值大小。
Sample
Input
10 9999
1 1 1 1 1 1 1 1 1 1
20 500
1 0 1 0 1 0 1 0 1 0
Output
45
104
思路
矩阵快速幂求解线性递推式
S
k
=
[
f
(
k
)
f
(
k
−
1
)
f
(
k
−
2
)
f
(
k
−
3
)
f
(
k
−
4
)
f
(
k
−
5
)
f
(
k
−
6
)
f
(
k
−
7
)
f
(
k
−
8
)
f
(
k
−
9
)
]
T
S_k=[f(k) \quad f(k-1) \quad f(k-2) \quad f(k-3) \quad f(k-4) \quad f(k-5) \quad f(k-6) \quad f(k-7) \quad f(k-8) \quad f(k-9) ]^T
Sk=[f(k)f(k−1)f(k−2)f(k−3)f(k−4)f(k−5)f(k−6)f(k−7)f(k−8)f(k−9)]T
S
9
=
[
f
(
9
)
f
(
8
)
f
(
7
)
f
(
6
)
f
(
5
)
f
(
4
)
f
(
3
)
f
(
2
)
f
(
1
)
f
(
0
)
]
T
S_9=[f(9) \quad f(8) \quad f(7) \quad f(6) \quad f(5) \quad f(4) \quad f(3) \quad f(2) \quad f(1) \quad f(0) ]^T
S9=[f(9)f(8)f(7)f(6)f(5)f(4)f(3)f(2)f(1)f(0)]T
S
k
=
A
k
−
9
S
9
S_k=A^{k-9} S_9
Sk=Ak−9S9
注意别忘了k<10的时候直接输出
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int maxm=1e5+5;
const int N=10;
int a[11],k,m;
int s[11];
long long ans;
struct Matrix {
long long f[N][N];
Matrix operator * (const Matrix& t) const {
Matrix r;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
r.f[i][j]=0;
for(int k=0;k<N;k++){
r.f[i][j]+=f[i][k]*t.f[k][j];
r.f[i][j]%=m;
}
}
}
return r;
}
Matrix(){memset(f,0,sizeof f);}
Matrix(const Matrix& t){memcpy(f,t.f,sizeof f);}
void show(){
for(int i=0;i<N;i++){
for(int j=0;j<N;j++)
cout<<f[i][j]<<" ";
cout<<endl;
}
cout<<endl;
}
};
Matrix quick_pow(Matrix t,int x){
Matrix r;
for(int i=0;i<N;i++)
r.f[i][i]=1;
while(x){
if(x & 1) r=r*t;
t=t*t;
x>>=1;
}
return r;
}
int main(){
for(int i=0;i<N;i++)
s[i]=9-i;
while(scanf("%d %d",&k,&m)!=EOF){
for(int i=0;i<N;i++)
scanf("%d",a+i);
Matrix r;
for(int j=0;j<N;j++)
r.f[0][j]=a[j];
for(int i=1;i<N;i++)
r.f[i][i-1]=1;
if(k<10)
printf("%d\n",a[k]%m);
else{
Matrix temp=quick_pow(r,k-9);
ans=0;
for(int i=0;i<N;i++)
ans += temp.f[0][i]*s[i]%m;
printf("%d\n",ans%m);
}
}
return 0;
}
D - Q老师染砖(选做)
衣食无忧的 Q老师 有一天突发奇想,想要去感受一下劳动人民的艰苦生活。
具体工作是这样的,有 N 块砖排成一排染色,每一块砖需要涂上红、蓝、绿、黄这 4 种颜色中的其中 1 种。且当这 N 块砖中红色和绿色的块数均为偶数时,染色效果最佳。
为了使工作效率更高,Q老师 想要知道一共有多少种方案可以使染色效果最佳,你能帮帮他吗?
Input
第一行为 T,代表数据组数。(1 ≤ T ≤ 100)
接下来 T 行每行包括一个数字 N,代表有 N 块砖。(1 ≤ N ≤ 1e9)
Output
输出满足条件的方案数,答案模 10007。
Sample
Input
2
1
2
Output
2
6
思路
状态转移方程:
A
[
i
]
A[i]
A[i]:红绿色均为偶数
B
[
i
]
B[i]
B[i]:红绿色均为奇数
C
[
i
]
C[i]
C[i]:红绿色一奇一偶
矩阵快速幂只是求解状态转移方程的一种优化,这道题的需要列出三个数组来求解DP,而不是像前面的例题一样只用了一个数组,这道题拓展了思路
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
const int N=3;
const int p=10007;
struct Matrix{
long long x[N][N];
Matrix operator* (const Matrix& t) const{
Matrix r;
for(int i=0;i<N;i++){
for(int j=0;j<N;j++){
r.x[i][j]=0;
for(int k=0;k<N;k++){
r.x[i][j]+=x[i][k]*t.x[k][j]%p;
}
r.x[i][j]%=p;
}
}
return r;
}
Matrix(){memset(x,0,sizeof x);}
Matrix(const Matrix& t){memcpy(x,t.x,sizeof x);}
void show(){
for(int i=0;i<N;i++){
for(int j=0;j<N;j++)
cout<<x[i][j]<<" ";
cout<<endl;
}
}
};
Matrix quick_pow(Matrix a,int x){
Matrix r;
for(int i=0;i<N;i++)
r.x[i][i]=1;
while(x){
if(x&1) r=r*a;
a=a*a;
x>>=1;
}
return r;
}
int t,n;
long long ans;
int a[3];
Matrix m,temp;
int main(){
scanf("%d",&t);
a[0]=2;a[1]=0;a[2]=2;
m.x[0][0]=2;m.x[0][2]=1;
m.x[1][1]=2;m.x[1][2]=1;
m.x[2][0]=2;m.x[2][1]=2;m.x[2][2]=2;
while(t--){
ans=0;
scanf("%d",&n);
if(n==1){
printf("2\n");continue;
}
temp=quick_pow(m,n-1);
// temp.show();
for(int i=0;i<N;i++){
ans+=temp.x[0][i]*a[i]%p;
}
printf("%d\n",ans%p);
}
}
E - Q老师度假(选做)
忙碌了一个学期的 Q老师 决定奖励自己 N 天假期。
假期中不同的穿衣方式会有不同的快乐值。
已知 Q老师 一共有 M 件衬衫,且如果昨天穿的是衬衫 A,今天穿的是衬衫 B,则 Q老师 今天可以获得 f[A][B] 快乐值。
在 N 天假期结束后,Q老师 最多可以获得多少快乐值?
Input
输入文件包含多组测试样例,每组测试样例格式描述如下:
第一行给出两个整数 N M,分别代表假期长度与 Q老师 的衬衫总数。(2 ≤ N ≤ 100000, 1 ≤ M ≤ 100)
接下来 M 行,每行给出 M 个整数,其中第 i 行的第 j 个整数,表示 f[i][j]。(1 ≤ f[i][j] ≤ 1000000)
测试样例组数不会超过 10。
Output
每组测试样例输出一行,表示 Q老师 可以获得的最大快乐值。
Sample
Input
3 2
0 1
1 0
4 3
1 2 3
1 2 3
1 2 3
Output
2
9
思路
这个题的状态转移方程比较好写:
d
p
[
i
]
[
j
]
=
m
a
x
{
d
p
[
i
−
1
]
[
k
]
+
f
[
k
]
[
j
]
}
dp[i][j]=max\{ dp[i-1][k] + f[k][j] \}
dp[i][j]=max{dp[i−1][k]+f[k][j]}
d
p
[
i
]
[
j
]
i
是天数,
j
是今天的衬衫
dp[i][j] \quad i\text{是天数,}j\text{是今天的衬衫}
dp[i][j]i是天数,j是今天的衬衫
而对于这个方程使用矩阵快速幂进行优化是这道题的拓展思路的地方
例如:
f
[
i
]
[
1
]
=
m
a
x
{
f
[
i
−
1
]
[
1
]
+
H
[
1
]
[
1
]
,
f
[
i
−
1
]
[
2
]
+
H
[
2
]
[
1
]
,
f
[
i
−
1
]
[
3
]
+
H
[
3
]
[
1
]
,
…
…
f
[
i
−
1
]
[
M
]
+
H
[
M
]
[
1
]
}
f[i][1]=max\{ f[i-1][1]+H[1][1] ,f[i-1][2]+H[2][1],f[i-1][3]+H[3][1], \dots \dots f[i-1][M]+H[M][1] \}
f[i][1]=max{f[i−1][1]+H[1][1],f[i−1][2]+H[2][1],f[i−1][3]+H[3][1],……f[i−1][M]+H[M][1]}
相当于矩阵乘法中求出结果矩阵中的一个数
需要注意的是,由于这时的”矩阵乘法“定义改变了,相应的单位矩阵也要变化,也就是在快速矩阵幂中的单位矩阵初始化要变一下,如果是用原来的单位矩阵,最后结果会比正确答案大一
还有这道题由于矩阵维数不确定,所以把矩阵的所有方法和变量都封装到了一个类中
代码
#include <iostream>
#include <stdio.h>
#include <string.h>
using namespace std;
int n,m;
int str[101][101];
class Matrix{
public:
Matrix(int num);
Matrix(Matrix& t);
Matrix quick_pow(int a);
void init();
void show();
long long **x;
Matrix operator* (const Matrix& t) const{
Matrix r(size);
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++){
r.x[i][j]=0;
for(int k=1;k<=size;k++){
r.x[i][j]=max(r.x[i][j],x[i][k]+t.x[k][j]);
}
}
}
return r;
}
private:
int size;
};
Matrix::Matrix(int num){
size=num;
x=new long long*[num+1];
for(int i=1;i<=num;i++){
x[i]=new long long[num+1];
}
}
Matrix::Matrix(Matrix& t){
size=t.size;
x=new long long*[size+1];
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++){
x[i][j]=t.x[i][j];
}
}
}
void Matrix::init(){
size=m;
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++){
x[i][j]=str[i][j];
}
}
}
Matrix Matrix::quick_pow(int a){
Matrix r(size);
// for(int i=1;i<=size;i++){
// for(int j=1;j<=size;j++)
// r.x[i][j]=1;
// }
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++)
r.x[i][j]=0;
}
while(a){
if(a&1) r=r * (*this);
*this = *this * *this;
a>>=1;
}
*this=r;
return r;
}
void Matrix::show(){
for(int i=1;i<=size;i++){
for(int j=1;j<=size;j++)
cout<<x[i][j]<<" ";
cout<<endl;
}
}
int main(){
while(scanf("%d %d",&n,&m)!=EOF){
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++)
scanf("%d",&str[i][j]);
}
Matrix temp(m);temp.init();
temp.quick_pow(n-1);
// temp.show();
long long ans=0,t=0;
for(int i=1;i<=m;i++){
for(int j=1;j<=m;j++){
t=max(t,temp.x[i][j]);
}
ans=max(ans,t);
}
cout<<ans<<endl;
}
return 0;
}