2021牛客5

题目链接https://ac.nowcoder.com/acm/contest/11256

B - Boxes

题目

有n个箱子,每个箱子里白球和黑球的可能性为1/2,开一个箱子需要花费wi,可以花费c元知道剩余盒子中黑球的数目,求最小花费

思路

一开始还是有点混乱的@_@

  1. 首先,只需要在最开始的时候花费c,这样就知道之后的黑球的数目
  2. 之后每次打开盒子全为黑或白的概率为 1 − 1 2 n − i 1-\frac{1}{2^{n-i} } 12ni1
  3. 把wi从小到大排序然后计算比较
    在这里插入图片描述

代码

#include<bits/stdc++.h>
using namespace std;

int n;
double w[100010],sum[10010],c;
int main()
{
    cin>>n>>c;
    for(int i=1;i<=n;i++)
    {
        cin>>w[i];
    }
    sort(w+1,w+n+1);
    double ans=c;

    for(int i=1;i<=n;i++)
    {
        ans+=sum[i-1]*pow(0.5,n-i+1);
        sum[i]=sum[i-1]+w[i];
    }
    printf("%.7f\n",min(sum[n],ans));
    return 0;
}


D - Double Strings

题目

在这里插入图片描述
感觉题目才是最难懂的(( ̄、 ̄))

思路

就是有两个字符串s1和s2,找到有多少个子序列满足:
① 两个子序列长度相等
② 第x位时,s1[x]<s2[x]
③ <x时,s1[i]==s2[i]
④ >x时,任意
在这里插入图片描述
然后可以模拟排列组合,或者用数组w表示(这个很容易卡内存,所以要使用int,在计算中间转换)

代码

#include<bits/stdc++.h>
#define mod 1000000007
#define maxx 5005
using namespace std;
typedef long long ll;
char s1[maxx],s2[maxx];
int dp[maxx][maxx],w[maxx][maxx];

int main()
{
    scanf("%s%s",s1+1,s2+1);
    int len1=strlen(s1+1);
    int len2=strlen(s2+1);
    dp[0][0]=1;
    w[0][0]=1; //一开始忘记了,我说咋不对
    for(int i=1; i<5005; i++)
    {
        dp[0][i]=dp[i][0]=1;
        w[0][i]=w[i][0]=1;
    }
    for(int i=1; i<=len1; i++)
    {
        for(int j=1; j<=len2; j++)
        {
            w[i][j]=(w[i][j-1]+w[i-1][j])%mod;
            //cout<<w[i][j]<<' ';
        }
         //cout<<endl;
    }
    for(int i=1; i<=len1; i++)
    {
        for(int j=1; j<=len2; j++)
        {
            if(s1[i]==s2[j])
                dp[i][j]=(dp[i-1][j]+dp[i][j-1])%mod;
            else
                dp[i][j]=((ll)dp[i-1][j]+dp[i][j-1]-dp[i-1][j-1])%mod; //在计算过程中间改
             // cout<<dp[i][j]<<' ';
        }
    // cout<<endl;
    }
    ll ans=0;
    for(int i=1; i<=len1; i++)
    {
        for(int j=1; j<=len2; j++)
        {
            if(s1[i]<s2[j])
            {
                ans+=((ll)dp[i-1][j-1]%mod*w[len1-i][len2-j]%mod)%mod;
            }
        }
    }
    cout<<(ans)%mod;
    return 0;
}


总结

1.注意scanf从第一位开始读的写法
2.模拟的代码 还没看

ll pow_m(ll a,ll k,ll p)
{
    ll ans=1;
    ll tmp=a%p;
    while(k)
    {
        if(k&1)ans=ans*tmp%p;
        tmp=tmp*tmp%p;
        k>>=1;
    }
    return ans;
}
ll C(ll n,ll m,ll p)
{
    if(m>n)return 0;
    ll a=1,b=1;
    for(int i=1;i<=m;i++)
    {
        a=a*(n+i-m)%p;
        b=b*i%p;
    }
    return a*pow_m(b,p-2,p)%p;
}
ll Lucas(ll n,ll m,ll p)
{
    ll ans=1;
    while(n&&m)
    {
        ans=ans*C(n%p,m%p,p)%p;
        n/=p;
        m/=p;
    }
    return ans;
}

H - Holding Two

签到,就是构造00110011,11001100,。。。,的矩阵

K - King of Range

题目

求区间满足(最大值-最小值>k)的个数

思路

对于1,4,3,2,5,当k=2时,如果找到一对符合的,那么这个数之后所有的区间都符合
如(1,4)>2,那么区间(1,3),(1,2),(1,5)肯定是符合的
所以只需要维护两个单调序列,里面是当前区间的最大值和最小值

代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;

const int maxn=1e7+100;
ll a[maxn];
ll qmax[maxn];
ll qmin[maxn];
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1; i<=n; i++)
    {
        cin>>a[i];
    }

    while(m--)
    {
        ll k;
        cin>>k;
        ll ans=0,l=1,h1=1,t1=0,h2=1,t2=0;
        for(int i=1; i<=n; i++)
        {
            while(h1<=t1 && a[i]>=a[qmax[t1]])
                t1--;
            while(h2<=t2 && a[i]<=a[qmin[t2]])
                t2--;
            qmax[++t1]=i;
            qmin[++t2]=i;
            while(h1<=t1 && h2<=t2 && a[qmax[h1]]-a[qmin[h2]]>k)
            {
                ans+=n-i+1;//计算权值
                l++;
                if(qmax[h1]<l)
                    h1++;
                if(qmin[h2]<l)
                    h2++;
            }
        }
        cout<<ans<<endl;
    }
    return 0;
}


/*

5 2
1 5 9 3 4
4
*/

下面是队列的做法

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+10;

int a[maxn];

int main()
{
    int n,m,k;
    scanf("%d%d",&n,&m);
    for (int i= 1;i<=n;i++)
        scanf("%d",&a[i]);
    while(m--)
    {
        cin>>k;
        deque<int> qMax,qMin;//单调队列
        ll ans=0;
        for(int l=1,r=1;r<=n;++r) 
        {
            while(qMax.size()&&a[qMax.back()]<a[r])//只要qmax最尾部的数小于a[r],就不断弹出
                qMax.pop_back();
            qMax.push_back(r);//然后将r放进去
            while(qMin.size()&&a[qMin.back()]>a[r])
                qMin.pop_back();
            qMin.push_back(r);

            while(a[qMax.front()]-a[qMin.front()]>k)
            {
                ans+=(ll)n-r+1;
                l++;
                if(qMax.front()<l)//向右移动l,如果队列最大值的编号小于l就弹出
                    qMax.pop_front();
                if(qMin.front()<l)
                    qMin.pop_front();
            }
        }
        printf("%lld\n",ans);
    }
}

J - Jewels

题目

在这里插入图片描述

思路

二分图最大匹配问题
https://www.cnblogs.com/logosG/p/logos.html

代码

三次方的写法

//DFS
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define M(a,b) memset(a,b,sizeof a)
const int N=450;
ll w[N][N],lx[N],ly[N],slk[N];
int lft[N],S[N],T[N],pre[N],last[N],nl,nr,n,m;
ll ans;
bool dfs(int x)
{
    S[x]=1;
    for(int i=1; i<=n; i++)
        if(!T[i])
        {
            if(lx[x]+ly[i]==w[x][i])
            {
                T[i]=1;
                last[i]=x;
                pre[lft[i]]=i;
                if(!lft[i]||dfs(lft[i]))
                {
                    lft[i]=x;
                    return 1;
                }
            }
            else if(slk[i]>lx[x]+ly[i]-w[x][i])
            {
                last[i]=x;
                slk[i]=lx[x]+ly[i]-w[x][i];
            }
        }
    return 0;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0);
    cin>>n;
    nl=nr=n;
    for(int i=1; i<=n; ++i)
    {
        int x,y,z,v;
        cin>>x>>y>>z>>v;
        ans+=x*x+y*y+(ll)(z+n*v)*(z+n*v);
        for(int j=0; j<n; ++j)
        {
            ll t=(ll)(z+n*v)*(z+n*v)-(ll)(z+j*v)*(z+j*v);
            w[i][j+1]=t;
            lx[i]=max(lx[i],t);
            //  cout<<w[i][j+1]<<' ';
        }
        // cout<<endl;
    }
    for(int i=1,u,y; i<=n; i++)
    {
        ll a;
        M(S,0);
        M(T,0);
        M(pre,0);
        M(last,0);
        M(slk,0x3f);
        for(u=i; u&&!dfs(u); pre[u=lft[y]]=y)
        {
            a=0x3f3f3f3f3f3f3f3fll;
            for(int i=1; i<=n; i++)
            {
                if(!T[i] && slk[i]<a)
                {
                    a=slk[i];
                    y=i;
                }
            }
            for(int i=1; i<=n; i++)
            {
                if(S[i])
                    lx[i]-=a;
                if(T[i])
                    ly[i]+=a;
                else slk[i]-=a;
            }
            T[y]=1;
        }
        for(; u!=i;)
        {
            u=pre[u];
            lft[u]=last[u];
            u=last[u];
        }
    }
    for(int i=1; i<=n; i++)
        ans-=lx[i]+ly[i];
    cout<<ans<<'\n';
    return 0;
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值