牛客小白月赛25 题解

一.总结

兰子姐姐的题还是比较能够让人看到希望的,但是由于 cout double 的 bug ,在F和G浪费了太多时间,还有各种细节都没注意到,都没时间做完。

本次怀疑人生:F/G/J

二.题解

A.AOE还是单体?

样例的解释明显告诉我们如何去计算,就是判断使用一次群体攻击的花费是否大于单独攻击 k 次的花费,然后我们就可以根据 x 来决定使用多少次群体攻击。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;

ll a[manx],s[manx];
// ll a[N][N];

int main(){
    io;
    ll n,x; cin>>n>>x;
    for(int i=1;i<=n;i++) cin>>a[i],s[i]=s[i-1]+a[i];
    sort(a+1,a+1+n);
    ll ans=0;
    if(x<=n){
        for(int i=n-x;i<=n;i++)
            ans+=a[i]-a[n-x];
        ans+=a[n-x]*x;
    }
    else ans=s[n];
    cout<<ans;
    return 0;
}

B.k-size字符串

F和G浪费太多时间导致没时间写 太菜了 赛后补的题

题目就是给 n 个字符 a 和 m 个字符 b,求 k size 字符。
很明显就是隔板问题,但是要注意有两种情况,一种是ab开头的类型,一种是ba开头的类型。
公式:

以 ab 为例子来说:

  1. a 的话就是有 n-1 个间隔(因为有 n 个 a),然后我们可以选取 个隔板放下去,这样就分成了 份 ;
  2. b 的话就是有 m-1 个间隔(因为有 m 个 b),然后我们可以选取 个隔板放下去,这样就分成了 份 ;

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;

ll a[70];

ll qp(ll a,ll b,ll p){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%p;
        a=a%p*a%p;
        b>>=1;
    }
    return ans%p;
}
ll Inv(ll x,ll p){return qp(x,p-2,p);}
ll Cal(ll n,ll m,ll p){
    if(m>n) return 0;
    ll ans=1;
    for(int i=1;i<=m;++i)ans=ans*Inv(i,p)%p*(n-i+1)%p;
    return ans%p;
}
int main(){
    ll n,m,k; cin>>n>>m>>k;
    if(n+m<k || k==1){
        puts("0");
        return 0;
    }
    ll ans=0;
    ans+= Cal(n-1,k/2-1,mod)*Cal(m-1,k-k/2-1,mod)%mod;
    ans%=mod;
    ans+= Cal(m-1,k/2-1,mod)*Cal(n-1,k-k/2-1,mod)%mod;
    cout<<ans%mod;
    return 0;
}

C.白魔法师

树形DP模板题,类似于没有上司的舞会。
表示以u为根的子树没有使用魔法得到的最大白色联通块的大小,表示以u为根的子树使用魔法得到的最大白色联通块的大小。
那么很明显,我们在 dfs 的时候,只要用一个变量 cnt 来记录没有使用魔法子树的白色联通块大小和,一个变量 sb 来记录使用了魔法最大的白色联通块大小,就可以得到公式:

有:
有:

在 dfs 的过程中 ans 取 max 就好了。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;

vector<ll>g[manx];
ll n,ans;
ll col[manx],dp[manx][4];
string s;

void dfs(ll u,ll pre){
    dp[u][0]=col[u];
    ll sb=-1e18,cnt=0;
    for(auto v:g[u]){
        if(v==pre) continue;
        dfs(v,u);
        cnt+=dp[v][0];
        sb=max(sb,dp[v][1]-dp[v][0]);
    }
    if(col[u]) dp[u][0]=cnt+1,dp[u][1]=dp[u][0]+sb;
    else dp[u][1]=cnt+1;
    ans=max(dp[u][1],ans);
    ans=max(dp[u][0],ans);
}

int main(){
    cin>>n; cin>>s;
    s=" "+s;
    for(int i=1;i<=n;i++)
        if(s[i]=='W') col[i]=1;
    for(int i=2;i<=n;i++){
        ll u,v; cin>>u>>v;
        g[u].pb(v); g[v].pb(u);
    }
    dfs(1,0);
    cout<<ans;
    return 0;
}

D.抽卡

概率数学题,算出不可能的概率,再用 1 减去就可以了。
注意减的时候可能出现负数,所以需要加个 mod 再取模。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;
const int mod=1000000007;

ll a[manx],b[manx];

ll qp(ll a,ll b,ll p){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%p;
        a=a%p*a%p;
        b>>=1;
    }
    return ans%p;
}
int main(){
    io;
    ll n; cin>>n;
    ll ans=1;
    for(int i=1;i<=n;i++) cin>>a[i];
    for(int i=1;i<=n;i++){
        cin>>b[i];
        ll x=(a[i]-b[i])*qp(a[i],mod-2,mod)%mod;
        ans=ans*x%mod;
    }
    cout<<(1-ans+mod)%mod;
    return 0;
}

E.点击消除

用栈模拟字符串匹配的过程,如果遇到相同的就把连续两个弹出去~

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;

char s[manx];
// ll a[N][N];

int main(){
    io;
    char c; ll top=0;
    while(cin>>c){
        s[++top]=c;
        if(top>=2&&s[top-1]==s[top]){
          //  cout<<top<<" "<<s[top]<<" "<<c<<endl;
            top-=2;
            continue;
        }
    }
    if(!top) cout<<0;
    else
        for(int i=1;i<=top;i++)
            cout<<s[i];
    return 0;
}

F.疯狂的自我检索者

怀疑人生第一题。

最小情况就是剩余m个人都给1分,最大情况就是剩余m个人都给5分。

但是注意坑点!!!取消了同步就不能用 puts和print 不然就会 wa !!老是忘记这个坑点,浪费了许久。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;

double a[manx];
// ll a[N][N];

int main(){
   // io;
    ll n; cin>>n;
    ll m; cin>>m;
    double ans=0;
    for(int i=1;i<=n-m;i++) cin>>a[i],ans+=a[i];
    double mi=ans+m*1.0,ma=ans+m*5.0;
    printf("%.5lf %.5lf",mi/n,ma/n);
    return 0;
}

G.解方程

怀疑人生第二题。

模板小数二分,就是判断二分的 mid 代入方程后是否比 c 大,本来代码是可以通过的,但是我用cout输出就一直wa,直到后来换成了print,好像在这题直接白给一个小时(20.13~21.21)?

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;
const int mod=1000000007;

ll a,b,c;
ll pd(double x){
    double ans=1.0;
    for(int i=1;i<=a;i++) ans*=x;
    ans+=b*1.0*log(x);
    return (ans-c)>(1e-7);
}

int main(){
   // io;
    cin>>a>>b>>c;
    double l=1.0,r=1e9;
    for(int i=1;i<=100;i++){
        double mid=(r+l)/2;
        if(pd(mid)) r=mid;
        else l=mid;
    }
    printf("%.8lf",l);
    return 0;
}

H.神奇的字母(二)

可以桶排,也可以 map 记录。

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;

ll a[manx];
// ll a[N][N];
map<char,ll>v;

int main(){
    io;
    char c,sb; ll ans=0;
    while(cin>>c){
        if(c>='a'&&c<='z') v[c]++;
        if(v[c]>ans) ans=v[c],sb=c;
    }
    cout<<sb;
    return 0;
}

I.十字爆破

好像做过很多次这种题目了。

  1. 每个点为 列贡献+行贡献-本点贡献
  2. 由于n*m<1e6, 所以可以开出2维数组,但是不能直接开 ,要开成

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;

// ll a[N][N];

int main(){
    io;
    ll n,m; cin>>n>>m;
    ll a[n+1][m+1],b[n+1],c[m+1];
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            a[i][j]=b[i]=c[j]=0;
    for(int i=1;i<=n;i++)
        for(int j=1;j<=m;j++)
            cin>>a[i][j],b[i]+=a[i][j],c[j]+=a[i][j];
    for(int i=1;i<=n;i++){
        for(int j=1;j<=m;j++){
            cout<<b[i]+c[j]-a[i][j]<<" ";
        }
        cout<<endl;
    }

    return 0;
}

J.异或和之和

..怀疑人生第三题,wa 了10次,赛后才发现在超出 int 的范围会变成0,应该写成

二进制的题目一般都是按位算贡献,在这道题我们可以通过计算 64 个二进制位出现的次数,然后用组合数学来计算对答案的贡献:

  1. 因为只有出现奇数次才会对答案有贡献,出现偶数次的话会抵消异或成 0 ,所以我们只考虑选取的 3 个数中在这个位为 1 的个数为 1 或者 3 的情况。
  2. 当该位出现次数大于等于1,设为 x,则有 个数在这个二进制上为 0 ,所以对答案的贡献为: ;
  3. 当该位出现次数大于等于3,设为 x, 所以对答案的贡献为: ;

代码:

#include<bits/stdc++.h>
#define mp make_pair
#define pb push_back
#define ll long long
#define fi first
#define se second
#define inf 0x3f3f3f3f
#define io std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)
using namespace std;

const int manx=1e6+5;
const int N=1e3+5;
const int mod=1e9+7;

ll a[70];

ll qp(ll a,ll b,ll p){
    ll ans=1;
    while(b){
        if(b&1) ans=ans*a%p;
        a=a%p*a%p;
        b>>=1;
    }
    return ans%p;
}
ll Inv(ll x,ll p){return qp(x,p-2,p);}
ll Cal(ll n,ll m,ll p){
    if(m>n) return 0;
    ll ans=1;
    for(int i=1;i<=m;++i)ans=ans*Inv(i,p)%p*(n-i+1)%p;
    return ans%p;
}
int main(){
    io;
    ll n; cin>>n; ll ma=0;
    for(int i=1;i<=n;i++){
        ll x; cin>>x; ma=max(ma,x);
        for(int j=0;j<=64;j++){
            ll sb=(1ll<<j);
            if(sb>x) break;
            if(sb&x) a[j]++;
        }
    }
    ll ans=0;
    for(int i=0;i<=64;i++){
        if(!a[i]) continue;
        ll x=(1ll<<i);
        if(x>ma) break;
        x%=mod;
        if(n-a[i]>=2&&a[i]>=1) ans=(ans+a[i]%mod*Cal(n-a[i],2,mod)%mod*x%mod)%mod;
        if(a[i]>=3)ans=(ans+Cal(a[i],3,mod)%mod*x%mod)%mod;
        //cout<<i<<" "<<ans<<endl;
    }
    cout<<ans%mod;
    return 0;
}
全部评论

相关推荐

Natrium_:这时间我以为飞机票
点赞 评论 收藏
分享
评论
点赞
收藏
分享
牛客网
牛客企业服务