Codeforces Round #604 (Div. 2) (题解)
A. Beautiful String (暴力)
题目大意:
给定一个字符串,只有 \(?a\ b\ c\ ?\) ,问是否存在一种将所有的 \(?\) 替换成 \(a\ b\ c\) ,使得任意相邻的字符不同的方法。
大致思路:
其实可以发现问号都可以通过枚举使得其合法,若出现不合法必然除去问好已经不合法,暴力枚举即可。
代码:
点击展开代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
char s[N];
int T;
char c[3]={'a','b','c'};
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d",&T);
while(T--){
scanf("%s",s+1);
int len=strlen(s+1);
for(int i=1;i<=len;i++){
if(s[i]=='?'){
for(int j=0;j<3;j++){
if(c[j]!=s[i-1]&&c[j]!=s[i+1]){
s[i]=c[j];break;
}
}
}
}
int flag=0;
for(int i=1;i<len&&flag==0;i++){
if(s[i]==s[i+1])flag=1;
}
if(flag){
puts("-1");
}
else printf("%s\n",s+1);
}
return 0;
}
B. Beautiful Numbers (思维)
题目大意:
给一个排列,定义 \(m\) 为存在一段连续的 \(1 - m\) 的排列,如 \(\{4,5,1,3,2,6\}\) ,就存在 \({1,3,2}\) ,为 \(3\) 的排列,其就不存在 \(m=2\) 的排列。问对于每一个 \(m\) 判断是否存在 \(1-m\) 的排列。
大致思路:
只要将每一个数字的位置记录下来为 \(pos\) 数组,例如 \(\{4,5,1,3,2,6 \}\) 的 \(pos\) 数组就是 \(\{3,5,4,1,2,6 \}\) 那么我们只要从前往后记录最大值 \(max\) 和最小值 \(min\) ,然后 \(max-min+1==i\) 就是存在 \(1-m\) 的排列。
代码:
点击展开代码
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+10;
set<int>s; // 用set维护最大值最小值,其实不用那么麻烦
int T;
int a[N];
int pos[N];
int n;
int ans[N];
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d",&T);
while(T--){
s.clear();
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&a[i]);
pos[a[i]]=i;
}
for(int i=1;i<=n;i++){
s.insert(pos[i]);
auto it=s.begin();
int v1=*it;
it=s.end();
it--;
int v2=*it;
if(v2-v1+1==i)ans[i]=1;
else ans[i]=0;
}
for(int i=1;i<=n;i++)printf("%d",ans[i]);
puts("");
}
return 0;
}
C. Beautiful Regional Contest (贪心)
题目大意:
给 \(n\) 个人的解题数,现在在发奖牌,金牌银牌铜牌各 \(g,s,b\) 各,要求金牌解题数大于银牌,\(...\) ,铜牌解题数大于无牌,且 \(g,s,b>0 \ \&\& \ g<s,g<b\) ,要求求出的合法的奖牌数,且最大。
大致思路:
一开始想多了,结果导致卡到了比赛结束。。。其实金牌的数量是确定,那么银牌只要取得g+1,然后通过相同解题数看看银牌最少要取多少个,看后计算铜牌最多能取多少个。判断合法性即可。
代码:
点击展开代码
#include<bits/stdc++.h>
using namespace std;
const int N=4e5+10;
int T;
int n;
int p[N];
int id[N],l[N],r[N],sum[N],cnt=0;
int qh(int L,int R){
return sum[R]-sum[L-1];
}
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d",&T);
while(T--){
cnt=0;
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%d",&p[i]);
}
reverse(p+1,p+n+1);
int temp=p[1];cnt++;l[cnt]=1;
for(int i=1;i<=n;i++){
if(p[i]==temp)id[i]=cnt;
else{
temp=p[i];
r[cnt]=i-1;
cnt++;
l[cnt]=i;id[i]=cnt;
}
}
r[cnt]=n;
for(int i=1;i<=cnt;i++)sum[i]=r[i]-l[i]+1,sum[i]=sum[i-1]+sum[i];
int mx=n/2;
if(mx<5){
puts("0 0 0");continue;
}
int flag=1;
int pp=id[n];
int g=sum[pp]-sum[pp-1];
int pos=l[pp];
int s=g+1;
temp=pos-s;
if(s+g>=n/2)flag=0;
int pp1=id[temp];
int pos1=l[pp1];
s=pos-pos1;
int sy=n-mx;
int pp2=id[sy];
int pos2=r[pp2]+1;
pp2++;
int b=pos1-pos2;
if(b<=0||s<=0||b<=g||s<=g)flag=0;
if(flag)printf("%d %d %d\n",g,s,b);
else puts("0 0 0");
}
return 0;
}
D. Beautiful Sequence (枚举)
题目大意:
给四个数, \(a,b,c,d\) ,表示 \(a\) 个 \(0\) , \(b\) 个 \(1\) ,\(c\) 个 \(2\) ,\(d\) 个 \(3\) ,要求将这些数排成一排,使得相邻的数差值为1,输出方案。
大致思路:
可以枚举每一个数字做为开头,然后就选择相邻的数字往后填,如果能填完,那么输出方案即可。因为若存在答案,以4种数字开头必然有一种是可以得到解的。
代码:
点击展开代码
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
int ans[N];
int flag=0;
vector<int>temp;
void dfs(int a,int b,int c,int d,int pos){
if(a+b+c+d==0){
flag=1;
puts("YES");
for(int v:temp)printf("%d ",v);
puts("");exit(0);
}
if(pos==0&&b)temp.push_back(1),dfs(a,b-1,c,d,pos+1);
if(pos==1){if(c)temp.push_back(2),dfs(a,b,c-1,d,pos+1);
else if(a)temp.push_back(0),dfs(a-1,b,c,d,pos-1);}
if(pos==2){if(d)temp.push_back(3),dfs(a,b,c,d-1,pos+1);
else if(b)temp.push_back(1),dfs(a,b-1,c,d,pos-1);}
if(pos==3&&c)temp.push_back(2),dfs(a,b,c-1,d,pos-1);
}
int a,b,c,d;
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d%d%d%d",&a,&b,&c,&d);
flag=0;
if(flag==0&&a){
temp.clear();
temp.push_back(0);
dfs(a-1,b,c,d,0);
}
if(flag==0&&b){
temp.clear();
temp.push_back(1);
dfs(a,b-1,c,d,1);
}
if(flag==0&&c){
temp.clear();
temp.push_back(2);
dfs(a,b,c-1,d,2);
}
if(flag==0&&d){
temp.clear();
temp.push_back(3);
dfs(a,b,c,d-1,3);
}
if(flag==0)puts("NO");
return 0;
}
E. Beautiful Mirrors (概率DP)
题目大意:
有 \(n\) 个镜子,每一个镜子有 \(p_i\) 的概率说美丽,现在我从第一个镜子开始问,如果它说美丽,那么我就在下一天再问下一个镜子,如果刚好到了第 \(n\) 个镜子,那么我就会开心。否则我就下一天再从第一个镜子开始问。问我开心需要的天数的期望值。
大致思路:
假设有 \(i\) 个镜子,开心需要的天数为 \(F_i\) ,那么 \(F_i=P_i*(F_{i-1}+1)+(1-P_i)*(F_{i-1}+F_i+1)\)
就是在第 \(F_{i-1}\) 天必然是美丽,现在有 \(P_i\) 的概率是美丽,那么我就直接开心了,就是 \(F_{i-1}+1\) 天,还有剩下的概率,我需要从新开始那么就需要 \(F_{i-1}+F_i+1\) 天了。将式子化简一下就是 \(F_i=(F_{i-1}+1)/P_i\) 循环一遍就行了。(就要概率要除100).
代码:
点击展开代码
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+10;
const int mod=998244353;
ll ksm(ll a,ll b){
ll res=1,t=a;
while(b){
if(b&1)res=(res*t)%mod;
t=(t*t)%mod;
b>>=1;
}
return res;
}
int n;
ll p[N],f[N];
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
scanf("%d",&n);
for(int i=1;i<=n;i++)scanf("%lld",&p[i]);
f[0]=0;
for(int i=1;i<=n;i++){
f[i]=((100*(f[i-1]+1)%mod)*ksm(p[i],mod-2)%mod)%mod; //注意爆ll
}
printf("%lld\n",f[n]);
return 0;
}
F. Beautiful Bracket Sequence (easy version) (DP)
题目大意:
给一个字符串,字母只有括号和 \(?\) ,问对于所有的 \(?\) 填法,其括号嵌套数之和是多少。括号嵌套数是指一个括号序列最多的嵌套数,不要求合法,即可以删除任意数量的括号后,保持相对位置不变的最大括号层数。例如对于:\((??)\) ,那么所有填法为 \((()),()(),())),((()\) ,这四种,答案就是 \(2+1+1+1=5\) 。
大致思路:
经过研究题解后,感觉大概懂了一些。用 \(dp[i][j]\) 表示区间 \([i,j]\) 的答案,那么我们考虑转移方程,如果 \(s[i]\ !='('\) ,那么对于区间 \([i,j]\) ,对答案的贡献必然是等于区间 \([i+1,j]\) 的,同理如果 \(s[j]\ !=')'\) ,那么对于区间 \([i,j]\) ,对答案的贡献必然是等于区间 \([i,j-1]\) 的,当然如果都不等还是要考虑容斥的问题。那么这时考虑 \(s[i],s[j]\) 为 \((),?),(?,??\) 的情况。那么对于 \([i,j]\) 来说必然会比 \([i+1,j-1]\) 多产生一点贡献,因为必然能出现 \(()\) 的情形,如果 \([i+1,j-1]\) 的问号数为 \(k\) ,那么就多 \(2^k\) 的贡献。那么转移方程就可以写出来。
\(dp[l][r]+=dp[l+1][r]\) ( \(s[l]\ !='('\))
\(dp[l][r]+=dp[l][r-1]\) ( \(s[r]\ !=')'\) )
\(dp[l][r]-=dp[l+1][r-1]\) (容斥)
\(dp[l][r]+=dp[l+1][r-]+2^k\) ( \(s[l]\ !=')' \&\& s[r]\ !='('\) )
代码:
点击展开代码
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int N=2e3+10;
const int mod=998244353;
ll dp[N][N];
int sum[N];
char s[N];
int js(int l,int r){
return sum[r]-sum[l-1];
}
ll p[N];
int main()
{
//freopen("H:\\c++1\\in.txt","r",stdin);
//freopen("H:\\c++1\\out.txt","w",stdout);
p[0]=1;
for(int i=1;i<N;i++)p[i]=(p[i-1]*2)%mod;
scanf("%s",s+1);
int n=strlen(s+1);
for(int i=1;i<=n;i++){
if(s[i]=='?')sum[i]=sum[i-1]+1;
else sum[i]=sum[i-1];
}
for(int i=1;i<=n;i++)dp[i][i]=0;
for(int len=2;len<=n;len++){
for(int l=1;l<=n+1-len;l++){
int r=len+l-1;
if(s[l]!='(')dp[l][r]=(dp[l][r]+dp[l+1][r])%mod;
if(s[r]!=')')dp[l][r]=(dp[l][r]+dp[l][r-1])%mod;
if(s[l]!='('&&s[r]!=')')dp[l][r]=(dp[l][r]-dp[l+1][r-1]+mod)%mod;
if(s[l]!=')'&&s[r]!='(')dp[l][r]=(dp[l][r]+dp[l+1][r-1]+p[js(l+1,r-1)])%mod;
}
}
printf("%lld\n",dp[1][n]);
return 0;
}