Educational Codeforces Round 57 (Rated for Div. 2) ABCDEF题解

题目总链接:https://codeforces.com/contest/1096

A. Find Divisible

题意:

给出l,r,在[l,r]里面找两个数x,y,使得y%x==0,保证有解。

 

题解:

直接输出l,2*l就好啦,但我还是写了个循环...

代码如下:

#include 
using namespace std;
typedef long long ll;
const int N = 10;
int T;
ll l,r;
int main(){
    cin>>T;
    while(T--){
        scanf("%I64d%I64d",&l,&r);
        for(ll i=l;i<=r;i++){
            if(i*2<=r){
                printf("%I64d %I64d\n",i,2*i);
                break ;
            }
        }

    }
    return 0;
}
View Code

 

B. Substring Removal

题意:

给出一个字符串,现在要你删掉一个子串,使得剩下的串有不多于一个的字符。

 

题解:

从两端找连续的相同字符串并且统计个数,然后算算就可以了。

注意一下所有字符串都相等的情况。

还有一种情况就是两段连续的字符串字符都相等,那么这时就可以删掉中间的,这个个数也比较好统计。

具体见代码(注意中间计算过程不要爆int):

#include 
using namespace std;
typedef long long ll;
const int N = 2e5+5,MOD = 998244353;
int n;
char s[N];
int main(){
    scanf("%d",&n);
    scanf("%s",s);
    char fir = s[0];
    int i;
    ll cnt1=1,cnt2=1;
    for(i=1;i){
        if(s[i]==fir) cnt1++;
        else break ;
    }
    char last = s[n-1];
    for(i=n-2;i>=0;i--){
        if(s[i]==last) cnt2++;
        else break ;
    }
    ll ans = (cnt1+cnt2+1)%MOD;
    if(last==fir){
        ans=(ans+cnt1*cnt2)%MOD;
    }
    cout<endl;
    return 0;
}
View Code

 

C. Polygon for the Angle

题意:

给出一个角度(0<=angle<180),求出最小的正多边形,满足其中存在一个内接三角形的内角与给出的角度相等。

 

题解:

我的做法比较暴力了,先说说我的吧:

n边形的内角和为(n-2)*180,然后可以知道其每个内角为(n-2)*180/n,利用外接圆+一些圆的性质(圆心角等于二倍圆周角)可以知道一个正n边形的内接三角形的最小角为180/n。

之后只要判断angle%(180/n)是否为0就是了。注意最小角可以为小数,所以我直接是暴力循环...

正解是这样的,首先推出最小角为180/n,然后如果满足angle%(180/n)==0的话,则有n*angle=t*180。

由于180和angle为已知量,我们就可以求出g=gcd(angle,180),然后等式则有n*angle/g = t*180/g。

由这可以知道n=x*180/g , t=y*angle/g,我们知道,这里的t满足t*180/n=angle,当t=n-2时,这时就是正多边形的内角,所以有限制条件:1<=t<=n-2。

我们取x=y=1,这里的n就是答案了(贪心),但是可能会存在这种情况:t=n-1,这时不符合限制条件的,这里我们只要取x=2就行了。

可以证明,当x>=2时,t<=n-2恒成立。

 

给出我的暴力代码....

#include 
using namespace std;
typedef long long ll;
int T;
int ang;
int main(){
    cin>>T;
    while(T--){
        scanf("%d",&ang);
        double ans ;
        int flag;
        for(int i=3;i<=360;i++){
            ans = (double)180/i;
            flag=0;
            if(180%i!=0) for(int j=1;;j++){
                if(j*ans>ang){
                    flag=1;
                    break ;
                }else if((double)j*ans==(double)ang){
                    cout<endl;
                    flag=2;
                    break ;
                }
            }
            if(flag==1) continue ;
            else if(flag==2) break ;
            if(ang%(int)ans==0&&ans*(i-2)>=ang){
                cout<endl;
                break ;
            }
        }
    }
    return 0;
}
View Code

 

D. Easy Problem

题意:

给出一个字符串,现在要求删去一些字符,使得字符串中不含"hard"子序列,删去一个字符有相应代价,问最小代价。

 

题解:

如果不存在"hard"子序列的话,我们只需要将其中一个字符删完就行。

我们考虑用动态规划来做,dp(i,j)表示长度为i的字符串中,将第j个删完的最小代价。因为对于每一个字符都有删与不删两种选择。

那么转移方程就为:if(s[j]=='r')(举例)dp(i,j)=min(dp(i,j-1),dp(i-1,j)+a[j]) ; else dp(i,j)=dp(i-1,j).

意思第一个方程的意思即为要么将"r"字符删完,要么将之前的"h"或者"a"的字符删完,求两者的最小代价。

最后求min(dp(n,0),dp(n,1),dp(n,2),dp(n,3))即可。

 

代码如下:

#include 
using namespace std;
typedef long long ll;
const int N = 1e5+5;
ll dp[N][5]; 
//dp(i,j):前i个位置,将第j个删完的最小代价。
char s[N];
ll a[N];
int n;
int main(){
    scanf("%d",&n);
    scanf("%s",s+1);
    for(int i=1;i<=n;i++) scanf("%d",&a[i]);
    for(int i=1;i<=n;i++){
        dp[i][0]=dp[i-1][0]+(s[i]=='h')*a[i];
        dp[i][1]=min(dp[i-1][0],dp[i-1][1]+(s[i]=='a')*a[i]);
        dp[i][2]=min(dp[i-1][1],dp[i-1][2]+(s[i]=='r')*a[i]);
        dp[i][3]=min(dp[i-1][2],dp[i-1][3]+(s[i]=='d')*a[i]);
    }
    cout<3],dp[n][2],dp[n][1],dp[n][0]});
    return 0;
}
View Code

 

E. The Top Scorer

题意:

给出p,s,r,p即人头数,s即每个人得分的总和,r即主人公的分数下限。

现在每个人都忘了自己的分数是多少,只有主人公知道自己的分数下限,问最后主人公获胜的概率是多少。

ps:当有多个人分数相同时,获胜概率为其除以相应人数。

 

题解:

这题全场A的人数最少,但也的确有点难度。

通过每个人去划分s,可以联想到“插板法”的计算方法。

我们可以看看一个类似的问题:x1+x2+x3+x4+x5=20,0<=xi<=5,问解的情况有多少种。

这实际上是有上界的问题,如果存在的话应该用容斥原理去解。

我们也可以将这个题限定一个上界,设g(s,p,m):p个人,分数和为s,每个人分数不超过m的情况数,这就类似于刚才说的问题。

主要问题是g的计算,我们首先算出总情况:C(s-1+p,p-1)。在这些情况中会有些不符合要求的情况,那么我们减去至少有一个人分数大于m的情况:C(p,1)*C(s-1+p-1*(m+1),p-1)。

这里会多减去一些情况(可以将每个人看做一个独立的集合),然后再加回来....

最后可以得出g的计算方法为:

然后观察一下数据范围,发现可以枚举,那么我们就枚举主人公的得分,再枚举有多少个人分数与之相同,最后计算一下就OK。

本体细节有点多,写的时候注意下,提示:组合。

 

代码如下:

#include 
using namespace std;
typedef long long ll;
const int N = 5005,MOD = 998244353;
ll p,s,r;
ll fac[N+N];
ll qp(ll a,ll b){
    ll ans = 1;
    while(b){
        if(b&1) ans=ans*a%MOD;
        a=a*a%MOD;
        b>>=1;
    }
    return ans ;
}
ll inv(ll x){
    return qp(x,MOD-2);
}
ll C(ll n,ll m){
    if(n==m) return 1;
    if(m>n) return 0;
    return fac[n]*inv(fac[m]*fac[n-m]%MOD)%MOD;
}
ll g(ll S,ll P,ll m){
    ll ans=0,tmp;
    if(!S && !P) return 1;  //!!
    for(int i=0;i<=P;i++){
        tmp = C(P,i)*C(S+P-1-i*(m+1),P-1)%MOD;
        if(i&1) ans-=tmp;
        else ans+=tmp;
        ans%=MOD;
        if(ans<0) ans+=MOD;
    }
    return ans ;
}
int main(){
    cin>>p>>s>>r;
    fac[0]=1;
    for(int i=1;i<=1e4;i++) fac[i]=fac[i-1]*i%MOD;
    ll w=0;
    for(int t=r;t<=s;t++){
        for(int q=1;q<=p;q++){
            if(s-q*t<0) continue ;
            w=(w+(C(p-1,q-1)*g(s-q*t,p-q,t-1)%MOD)*inv(q)%MOD)%MOD;
        }
    }
    cout<1-r+p,p-1))%MOD;
    return 0;
}
View Code

 

F. Inversion Expectation

题意:

给你一个1-n的排列,但现在缺失了一些数字,现在要求逆序对个数的期望值。

 

题解:

注意这里是期望,期望是线性的,可以分开来算。

这个题主要考虑四种情况:(假设-1的个数为cnt)

1.未知与未知。这种情况的期望值为(cnt-1)*cnt/4,因为对于一个排列,都会存在另一个与之相反的排列,他们的总逆序对个数为(cnt-1)*cnt/2。我们就可以看作每个排列的逆序对个数为(cnt-1)*cnt/4。

2.左边未知与右边已知;

3.左边已知与右边未知。

上面两种情况思考的方式都是一样的,拿第二种情况来说吧,我们只需要统计未出现的数字中有多少个比当前这个已知数大(假设为num),以及当前已知数左边有多少个位置为-1(假设为len)。

那么对于每个位置,产生一个逆序对的概率为num/cnt,其期望也为num/cnt,那么对于当前这个已知数,它对答案的贡献为len*num/cnt。

另外一种情况同样的求法。

4.已知与已知。这时的期望值就是逆序对的个数,用树状数组或者归并排序求一下就好了。

 

代码如下:

#include 
using namespace std;
typedef long long ll;
const int N = 2e5+5,MOD = 998244353;
int n;
int a[N],c[N],large[N],vis[N];
int lowbit(int x){
    return x&(-x);
}
ll qp(ll A,ll B){
    ll ans=1;
    while(B){
        if(B&1) ans=ans*A%MOD;
        A=A*A%MOD;
        B>>=1;
    }
    return ans ;
}
ll inv(ll x){
    return qp(x,MOD-2);
}
void update(int p,int x){
    for(;p<=n;p+=lowbit(p)) c[p]+=x;
}
ll getsum(int p){
    ll cnt = 0;
    for(;p>0;p-=lowbit(p)) cnt+=c[p];
    return cnt ;
}
int main(){
    scanf("%d",&n);
    ll cnt = 0,ans =0,num;
    for(int i=1;i<=n;i++){
        scanf("%d",&a[i]);
        if(a[i]==-1) cnt++;
        else vis[a[i]]=1;
    }
    int cur = 0;
    for(int i=n;i>=1;i--){
        large[i]=cur;
        cur+=!vis[i];
    }
//Case1:
    ans=(ans+(cnt-1)*cnt%MOD*inv(4)%MOD)%MOD;
//Case2:
    num = 0;
    for(int i=1;i<=n;i++){
        if(a[i]==-1) num++;
        else{
            ll len = large[a[i]];
            ans=(ans+num*len%MOD*inv(cnt)%MOD)%MOD;
        }
    }
//Case3:
    num = 0;
    for(int i=n;i>=1;i--){
        if(a[i]==-1) num++;
        else{
            ll len = cnt-large[a[i]];
            ans=(ans+num*len%MOD*inv(cnt)%MOD)%MOD;
        }
    }
//Case4:
    for(int i=n;i>=1;i--){
        if(a[i]==-1) continue ;
        ans=(ans+getsum(a[i]))%MOD;
        update(a[i],1);
    }
    cout<<ans;
    return 0;
}
View Code

 

最近事情好多啊~马上又要期末考了,也要稍微准备下了,再不复习就滑铁卢了啊啊啊。

转载于:https://www.cnblogs.com/heyuhhh/p/10211421.html

你可能感兴趣的:(数据结构与算法)