传送门:CF
A题 Prefix and Suffix Array:
这道题的关键在于通过所有的字符前缀和后缀来找到题目所给出的字符串是是什么.当我们的出了字符串之后,判断回文就十分简单了
对于所有前后缀,我们会发现只要找到两个长度为
n
−
1
n-1
n−1的字符即可,因为对于长度为
n
−
1
n-1
n−1的字符只有两个并且只要我们合并这两个字符就得出了原本的字符串
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int main() {
int T=read();
while(T--) {
int n=read();
string s;string s1="",s2="";
int cnt=1;
while(cnt<=2*n-2) {
cin>>s;
if(s.length()==n-1) {
if(s1=="") {
s1=s;
}
else{
s2=s;
}
}
cnt++;
}
string lasts="";
if(s1.substr(1,n-2)==s2.substr(0,n-2)) {
lasts=s1+s2.substr(n-2,1);
}
else {
lasts=s2+s1.substr(n-2,1);
}
string last2=lasts;reverse(last2.begin(),last2.end());
if(last2==lasts) cout<<"Yes"<<endl;
else cout<<"No"<<endl;
}
}
B题 Not Dividing
对于本题我是简单的猜想了一个解决方法,从前往后进行遍历,如果后面的数字能被前一个数字整除,那么我们将后面的数字+1,这样就解决了整除问题了.当然对于前一个位置是1的时候,我们就应该改变1的值,将1改为2,然后假设当前的数字能被2整除就将当前的数字+1即可
除了上述,代码中还存在一些细节问题,具体请参考代码
我们发现对于每一个数字我们都只改变一次即可搞定整除问题,所以该解决方法满足题意
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int T;int a[maxn];int ans[maxn];
int main() {
T=read();
while(T--) {
int n=read();
for(int i=1;i<=n;i++) {
a[i]=read();
}
for(int i=1;i<=n;i++) {
if(i==1) {
if(a[i]==1) {
a[i]+=1;
}
ans[i]=a[i];
continue;
}
if(a[i]%a[i-1]==0) {
a[i]++;
}
else {
if(a[i]==1) {
if(a[i-1]==2) {
a[i]=3;
}
else {
a[i]=2;
}
}
}
ans[i]=a[i];
}
for(int i=1;i<=n;i++) printf("%d ",ans[i]);
printf("\n");
}
return 0;
}
C题 C. Scoring Subsequences
本题需要一些思维进行解决,符合CF的C题思维题的想法
对于一串序列,我们需要找到满足题意的最大的最长子序列.我们先想一下如何满足最大.题目中给了大小不下降的这一个条件,显然就应该从这里来着手.我们发现分母部分是 d ! d! d! d \quad d d是我们的长度.我们想最大,贪心的去想一下,对于任意的长度 k k k来说,我们的分母的大小是确定的,所以我们想保证最大就需要分子最大,那么我们应该从大往小进行选取字母.
到此这道题的解法就呼之欲出了.对于一段序列,我们从后往前进行枚举,然后我们的长度不断增加,也就是分母的权重每一次都增加一个当前长度的贡献,那么显然的,如果当前我们分子上增加的 a [ i ] a[i] a[i]如果比不上分母增加的贡献的话,此时我们肯定是在减少的.所以我们现在的目标就是判断当前长度的端点和长度的关系就可.我们可以使用二分来解决,二分长度.然后再判断一下 a [ i − ( m i d − 1 ) ] a[i-(mid-1)] a[i−(mid−1)]是不是满足即可
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define maxn 1000000
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
int T;int a[maxn];
vector<int>v;
int main() {
T=read();
while(T--) {
int n=read();v.clear();
for(int i=1;i<=n;i++) a[i]=read();
printf("1 ");v.push_back(1);
for(int i=2;i<=n;i++) {
int l=1,r=i;
int ans=1;
while(l<=r) {
int mid=(l+r)>>1;
if(a[i-(mid-1)]>=mid) {
l=mid+1;
ans=mid;
}
else {
r=mid-1;
}
}
printf("%d ",ans);
}
printf("\n");
}
return 0;
}
D题 Counting Factorizations
一道组合数的题目.
对于给定的所有序列来说,显然只有质数是可以作为底数的,所以我们需要找出所有的质数,然后对于每一种质数来说,我们可能有多个,但是因为质因数分解的性质我们只能使用一个.
为了方便起见,我们预处理出每一个数的个数.对于每一个质数的个数我们使用 c i ci ci来表示,对于不是质数的个数我们使用 b i bi bi来表示.
我们假设选出了n个质数来作为底数(当然,当我们的质数的个数不及n个的时候是无解的,此时假设有解).因为我们挑出了质数,所以此时我们用
d
i
di
di来表示剩下来质数的个数,那么此时我们的总个数应该就是
n
!
(
b
1
!
∗
b
2
!
∗
b
3
!
∗
.
.
.
∗
b
k
1
!
)
∗
(
d
1
!
∗
d
2
!
∗
.
.
.
∗
d
k
2
!
)
\frac{n!}{(b1!*b2!*b3!*...*bk1!)*(d1!*d2!*...*dk2!)}
(b1!∗b2!∗b3!∗...∗bk1!)∗(d1!∗d2!∗...∗dk2!)n!
我们发现对于
n
!
b
1
!
∗
b
2
!
∗
b
3
!
∗
.
.
.
∗
b
k
1
!
\frac{n!}{b1!*b2!*b3!*...*bk1!}
b1!∗b2!∗b3!∗...∗bk1!n!是一个定值,我们可以直接计算出来
所以此时我们的问题就是如何计算出每一种选取方法的
∏
d
i
\prod\limits{d_i}
∏di值即可
对于这个问题我们可以使用
d
p
dp
dp来进行解决.我们使用
d
p
[
i
]
[
j
]
dp[i][j]
dp[i][j]来表示前
i
i
i个质数选取
j
j
j个质数的每一种情况的
∏
1
i
d
k
\prod\limits_{1}^{i}{d_k}
1∏idk的总和(这里很多题解都讲的不是很清楚),
那么对于第
i
i
i位来说,我们可以选择选取第
i
i
i个质数,此时我们的
d
k
=
c
k
−
1
dk=ck-1
dk=ck−1,此时我们会发现此时我们可以由前一位进行递推,因为对于前
i
−
1
i-1
i−1个质数选择
j
−
1
j-1
j−1的每一种情况,都可以通过选择当前质数来达到
j
j
j的情况,所以当前的总和就等于之前的总和乘上
d
k
dk
dk
假设不选取,
d
k
=
c
k
dk=ck
dk=ck,那么当前总和就等于之前的总和乘上
d
k
dk
dk
所以 d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] ∗ ( c k − 1 ) + d p [ i − 1 ] [ j ] ∗ ( c k ) dp[i][j]=dp[i-1][j-1]*(ck-1)+dp[i-1][j]*(ck) dp[i][j]=dp[i−1][j−1]∗(ck−1)+dp[i−1][j]∗(ck)
本题需要使用逆元,具体参考代码
下面是具体的代码部分:
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
#define root 1,n,1
#define ls rt<<1
#define rs rt<<1|1
#define lson l,mid,rt<<1
#define rson mid+1,r,rt<<1|1
inline ll read() {
ll x=0,w=1;char ch=getchar();
for(;ch>'9'||ch<'0';ch=getchar()) if(ch=='-') w=-1;
for(;ch>='0'&&ch<='9';ch=getchar()) x=x*10+ch-'0';
return x*w;
}
#define int long long
#define maxn 1000010
const double eps=1e-8;
#define int_INF 0x3f3f3f3f
#define ll_INF 0x3f3f3f3f3f3f3f3f
const int mod=998244353;
int n;int a[maxn];int fac[maxn],infac[maxn];
int check(int x) {
if(x==1) return false;
for(int i=2;i<=__builtin_sqrt(x);i++) {
if(x%i==0) return false;
}
return true;
}
set<int>prime;set<int>in_prime;
map<int,int>mp;int dp[4400][4400];
int qpow(int x,int y) {
int ans=1;
while(y) {
if(y&1) ans=(ans*x)%mod;
y>>=1;
x=(x*x)%mod;
}
return ans;
}
signed main() {
n=read();
for(int i=1;i<=2*n;i++) {
a[i]=read();
if(check(a[i])) prime.insert(a[i]);
else in_prime.insert(a[i]);
mp[a[i]]++;
}
fac[0]=1;infac[0]=1;
for(int i=1;i<=1e6+1;i++) {
fac[i]=fac[i-1]*i%mod;
infac[i]=infac[i-1]*qpow(i,mod-2)%mod;
}
int ans=fac[n];
for(auto i : in_prime) {
ans=(ans*infac[mp[i]])%mod;
}
int cnt=0;dp[0][0]=1;
for(auto i : prime) {
cnt++;
dp[cnt][0]=dp[cnt-1][0]*infac[mp[i]]%mod;
for(int j=1;j<=min(cnt,n);j++) {
dp[cnt][j]=(dp[cnt-1][j-1]*infac[mp[i]-1]%mod+dp[cnt-1][j]*infac[mp[i]]%mod)%mod;
}
}
ans=(ans*dp[cnt][n])%mod;
cout<<ans<<endl;
return 0;
}