牛客周赛35

A

A

小红的字符串切割

题意:

签到题

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+20;
ll n;
int main()
{
    string s;
    cin >> s;
    for(int i = 0 ; i < s.size() ; i++) {
        if(i == s.size() / 2)
            cout<<"\n";
        cout << s[i];
        
    }
    return 0;
}

B

B小红的数组分配

题意:

首先如果要实现原数组平分而且两个数组每个位置的值是相等的,必须满足所有原数组出现过的值必须是出现数量是偶数

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=2e5+20;
ll n;
int main()
{
    cin >> n;
    set<ll>ans;
    map<ll,ll>cnt;
    ll x;
    for(int i  = 1 ;i <= 2*n ; i++)
    {
        cin >> x;
        ans.insert(x);
        cnt[x]++;
        
    }
    vector<ll>res;
    for(auto it:ans)
    {
        if(cnt[it]%2!=0)
        {
            cout<<-1;
            return 0;
        }
        for(int i = 1 ; i <= cnt[it] / 2 ; i++)
            res.push_back(it);
    }
    for(int i = 0 ; i < res.size() ; i++)
    {
        cout<<res[i]<<" ";
    }
    cout<<"\n";
    for(int i = 0 ; i < res.size() ; i++)
    {
        cout<<res[i]<<" ";
    }
    return 0;
}

C

C小红关鸡

题意:

这题我先排了个序,我遍历了每个点,然后而二分查找第一个与改点距离大于等于k的点,然后定义一个ans保证是最大的数量

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+20;
ll a[N];
ll n,k;
int main()
{
    cin >> n >> k;
    for(int i = 1 ; i <= n ; i++)
    {
        cin >> a[i];
    }
    sort(a+1,a+1+n);
    ll ans = -1;
    for(ll i = 1 ; i<= n ; i++)
    {
        ll pos = upper_bound(a+1,a+1+n,a[i]+k)-a;
        if(a[pos] == a[i]+k)
        {
            ans = max(ans,pos-i+1);
        }
        else
        {
            ans = max(ans,pos-i);
        }
        if(a[i] + k >= a[n])
            break;
    }
    printf("%.4lf",1.0*ans/n);
    return 0;
}

D

D小红的排列构造

题意:

通过观察可以发现,数组唯一有问题的值只有两种 1.已经出现过一次 2.大于n的数,我们只要把这些有问题的数的位置记录下来,没问题的数放到set里面,然后遍历set,缺哪个值我们就用之前记录的位置去补。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
const int N=1e5+20;
ll a[N];
ll n,k;
int main()
{
    cin >> n;
    map<ll,ll>cnt;
    vector<ll>res;
    for(int i = 1; i <= n ; i++)
    {
        cin >> a[i];
        if(cnt[a[i]]>=1 || a[i] > n)
        {
            res.push_back(i);
        }
        cnt[a[i]]++;
    }
    vector<ll>ans;
    cout<<res.size()<<"\n";
    for(int i = 1 ; i <= n ; i++)
    {
        if(cnt[i]==0)
            ans.push_back(i);
    }
    for(int i = 0 ; i < res.size() ; i++)
    {
        cout<<res[i] <<" "<<ans[i]<<"\n";
    }
    return 0;
}

E

E小红的无向图构造

题意:

首先因为是最短我们肯定选择直接连,然后就可以发现他是一个分层图,每个点到1号点的最短路距离其实就是这个点所在的层数,当然我们排层次,我们要先去从小到大排序,这里我们拿vector<ll>e[n] 来存每个层数的点,我们第一想到的-1情况肯定是如果前一个层次没有点,我们无法构造这个层次的点。然后我们开始构造,从层次1开始,这里我们下层连上层的适合我们下层次就只要连上层次的第一个点,这样比较清晰哪个已经连过了,方便后添加边的处理,就这样连起来,然后最后如果m(能添加的边的数量)如果小于当前加边的数量,那肯定-1,如果直接等于当前连边的数量,那非常好,直接输出我们连的边就好。如果当前加的边小于m的话我们得考虑如何加边不影响这些点到1号点的最短路了。1.同层次的点互相连2.层次数差1的可以连起来(作者赛时也没想到)。在每加一条边的适合都判断加的边是不是已经等于m了,然后直接输出,如果所有能加的边全部都加上还小于m,那么也是-1的情况。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define PII pair<ll,ll>
const int N=1e5+20;
vector<ll>e[N];
ll a[N];
ll n,m;
int main()
{
    cin >> n >> m;
    for(int i = 1 ;i <= n ; i++)
    {
        cin >> a[i];
        e[a[i]].push_back(i);
    }
    ll maxn = *max_element(a+1,a+1+n); //求最大距离
    for(int i = 1 ; i <= maxn ; i++)
    {
          if(e[i].size() == 0)
          {
              cout<<-1;
              return 0;
          }
    }
    vector<PII>ans;
    for(int i = 1 ;i <= maxn ; i++)
    {
        int u = e[i-1][0];
        for(auto it : e[i])
        {
            ans.push_back({u,it});
        }
    }
    if(ans.size() > m)
    {
        cout<<-1;
        return 0;
    }
    m-=ans.size();
    if(m > 0)
    {
        for(int i = 1 ; i <= maxn ; i++)
        {
            for(int j = 0 ;j < e[i].size() ; j++)
            {
                int u = e[i][j];
                for(int k = j+1 ; k < e[i].size() ; k++)
                {
                    int v = e[i][k];
                    ans.push_back({u,v});
                    m--;
                    if(m==0)
                        break;
                }
                if(m == 0)
                    break;
            }
            if(m == 0)
                break;
        }
    }
    if(m > 0)
    {
        for(int i = 2; i <= maxn ; i++)
        {
            if(e[i-1] .size() - 1 == 0)
            {
                continue;
            }
            for(int j = 1 ;j < e[i-1].size() ; j++)
            {
                for(auto it:e[i])
                {
                    ans.push_back({e[i-1][j],it});
                    m--;
                    if(m==0)
                        break;
                }
                if(m == 0)
                    break;
            }
            if(m == 0)
                break;
        }
    }
    if( m > 0)
    {
        cout<<-1;
    }
    else
    {
        for(auto it :ans)
        {
            cout<<it.first<<" "<<it.second<<"\n";
        }
    }
    return 0;
}

 

F/G

F小红的子序列权值和(easy)
G小红的子序列权值和(hard)

题意:

由于只有n的范围的区别,我们直接讲hard版本,首先我们必须了解一下乘法原理如果一个数n=p1^q1*p2^q2 * p3 ^q3,那么n的因数个数则有(q1+1)*(q2+1)*(q3+1)。本题是非空子序列之和,我们不需要考虑顺序问题,由于1不影响乘法最终的结果,我们只需要考虑有多少个2和又多少个3,这里我们用双重循环去遍历所有的情况,这里选取的时候则用到了组合数的原理,例举一种情况:2取2个,3取2个,1有5个(1的数量虽然不影响但他有2^cnt[1]种选法)这种情况对答案的贡献就是 (2+1)*(2+1)%mod*2^5%mod*C[cnt[2]][2]%mod*C[cnt[3]][2]%mod(C[i][j]为组合数,cnt[i]为i在数组中出现的次数,在求组合数的时候需要用到逆元,否则无法求1e5的数据,最后这个双重循环可以优化一下,例如a1*b1+a1*b2+a2*b1+a2*b2,我们可以优化成 (a1+a2) * (b1+b2)。

代码:

#include <bits/stdc++.h>
using namespace std;
#define ll long long
#define PII pair<ll,ll>
const int N=1e5+20;
const ll mod = 1e9+7;
vector<ll>e[N];
ll n,m;
ll fastmi(ll a, ll b)
{
    ll res = 1 ;
    while(b)
    {
        if(b&1)
        {
            res = res * a % mod;
        }
        b>>=1;
        a = a * a % mod ;
    }
    return res;
}
ll jc[100100];
ll inv (ll x)
{
    return fastmi(x,mod-2);
}
ll c(ll x, ll y)
{
    return jc[x] * inv(jc[x-y]) % mod * inv(jc[y]) %mod;
}
int main()
{
    cin >> n;
    map<ll,ll>cnt;
    ll x;
    for(int i = 1 ; i <= n ; i++)
    {
        cin >> x;
        cnt[x]++;
    }
  //  cout<<fastmi(2,4)<<"\n";
    ll ans = 0;
    ll tmp = 1;
    jc[0] = 1;
    jc[1] = 1;
    for(int i = 2 ; i <= n ; i++)
        jc[i] = (jc[i-1]*i)%mod;
    for(int i = 1 ; i <= cnt[1] ; i++)
        tmp=(tmp*2)%mod;
    ll c2 = 0;
    ll c3 = 0;
    for(int i = 0 ; i <=cnt[2] ; i++)
    {
        c2 += c(cnt[2],i)*(i+1)%mod;
        c2%=mod;
    }
    for(int i = 0 ; i <=cnt[3] ; i++)
    {
        c3 += c(cnt[3],i)*(i+1)%mod;
        c3%=mod;
    }
    ans = c2 * c3 % mod *tmp % mod- 1 ;
   cout << ans;
    return 0;
}

ps:线性求逆元还不会,等亿会儿就补。

  • 22
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值