牛客练习赛75

A 费马小定理:p为质数时, a p − 1 ≡ 1 a^{p-1}≡1 ap11(mod p)。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N =  1e6+9, M = 4e5+10;
int a,b,m,n;
ll ksm(ll a,ll b){
    ll res=1;
    while(b){
        if(b&1) res=res*a%mod;
        a=a*a%mod;
        b>>=1;
    }
    return res;
}
ll f[N];
int main(){
    f[1]=f[2]=1;
    scanf("%d%d%d%d",&a,&b,&m,&n);
    for(int i=3;i<=n;++i) f[i]=(a*f[i-1]+b*f[i-2])%(mod-1);
    cout<<ksm(m,f[n])%mod;

}

B
当 k=0时,直接完全背包就好了;
否则,可以将最大的魔力交换给最小的抗力,那么这个魔法树一定是最优的哪一个,但是当n=2时,每次只能交换这两棵树,交换之后的结果是一定的,最大的魔力不一定和最小的抗力匹配,所以也得跑完全背包。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N =  1e3+9, M = 4e5+10;
int a[N],b[N],m,n,k;
ll f[N];
void get(){
    for(int i=1;i<=n;++i){
        for(int j=a[i];j<=m;++j)
            f[j]=max(f[j],f[j-a[i]]+b[i]);
    }
}

int main(){
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;++i) scanf("%d",a+i);
    for(int i=1;i<=n;++i) scanf("%d",b+i);
    if(k==0){
        get();
        cout<<f[m]<<'\n';
    }
    else if(n==2){
        if(k&1) swap(b[1],b[2]);
        get();
        cout<<f[m]<<'\n';
    }
    else{
        int mi=1e9+7,mx=0;
        for(int i=1;i<=n;++i) mi=min(mi,a[i]),mx=max(mx,b[i]);
        cout<<1ll*(m/mi)*mx<<'\n';
    }
}

C 当你走到第i个节点时时间不超过t时,你就可以将第i个点的所有宝石都捡起来。所以分别枚举以哪个点作为终点可以拿到最多宝石时,一定是将该点前距离最近的宝石全拿上,再拿上该点的即可。要想求前面最多拿多少个可以双指针。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N =  6e7+9, M = 4e5+10;
ll a[N],b[N],n,type;
ll t;
ll sum[N];

int main(){
    scanf("%lld%lld%lld",&n,&t,&type);
    if(type==1){
        for(int i=1;i<=n;++i) scanf("%lld",a+i);
    }else{
        ll p;
        scanf("%lld%lld",a+1,&p);
        for(int i=2;i<=n;++i){
            a[i]=a[i-1]^(a[i-1]<<13);//a数组要开ll,否则会爆
            a[i]=a[i]^(a[i]>>17);
            a[i]=(a[i]^(a[i]<<5))%p+1;
        }
    }
    ll ans=0;
    ll res=0;//区间[j+1,i]全部搬到点i需要的天数
    for(int i=1;i<=n;++i) sum[i]=sum[i-1]+a[i];
    for(int i=1,j=0;i<=n;++i){
        while(res>t){//超过t天,将a[j+1]全部丢掉
            res-=(ll)(a[j+1]*(i-j-1));
            j++;
        }
        ll tmp=min((a[j]),(t-res)/(i-j));//从j点最多搬多少个宝石才不超过t天
        ans=max(ans,tmp+sum[i]-sum[j]);
        res+=(sum[i]-sum[j]);//整个区间内的都搬到点i+1
    }
    printf("%lld\n",ans);
    return 0;
}

D 每次都将最小的两个合并,当合并之后的值比当前最大值大的时候,这个新值一定是加到队列末尾,后面产生的新值也会比这个值还要大,就是说按照当前的队列顺序去操作了。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N =  5e5+9, M = 4e5+10;
int a[N],b[N],m,n,k;

priority_queue<ll,vector<ll>,greater<ll> > q;
int max_val;
int main(){
    scanf("%d%d",&n,&k);
    for(int i=1;i<=n;++i) scanf("%d",a+i),q.push(a[i]),max_val=max(max_val,a[i]);
    while(q.size()>1){
        ll l=q.top();q.pop();
        ll r=q.top();q.pop();
        l=l*r+k;
        q.push(l);
        if(l>max_val) break;
    }
    int len=0;
    for(int i=1;;++i){
        if(q.size()==0) break;
        len++;
        a[i]=q.top()%mod;q.pop();
    }
    for(int i=1;i<len;i+=2){
        a[++len]=(1ll*a[i]*a[i+1]+k)%mod;
    }
    cout<<a[len]<<'\n';
}

E f[i][j] 表示:第i天处于j级时的最大金矿。两种转移方式:升级与不升级。
不升级的话又分为 正在挖矿还未挖到 和 已经挖到 两种情况。

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7;
const int N =  5e3+9, M = 4e5+10;
int a[N];
int p,c,n,m,t;
ll w[N],v[N],s[N];
ll f[N][N];
int main(){
    scanf("%d%d%d%d%d",s+1,v+1,&n,&m,&t);
    n++;
    for(int i=2;i<=n;++i) scanf("%d",w+i);
    for(int i=2;i<=n;++i) scanf("%d",v+i),v[i]+=v[i-1];
    for(int i=2;i<=n;++i) scanf("%d",s+i);
    memset(f,-1,sizeof f);
    f[0][1]=m;
    for(int i=0;i<=t;++i)
        for(int j=1;j<=n;++j){
            if(f[i][j]==-1) continue;
            if(j<n) f[i][j+1]=max(f[i][j+1],f[i][j]-w[j+1]);//升级
            f[i+1][j]=max(f[i+1][j],f[i][j]);//未挖到
            if(i+s[j]<=t) f[i+s[j]][j]=max(f[i+s[j]][j],f[i][j]+v[j]);//挖到了
        }
    ll ans=0;
    for(int i=1;i<=n;++i) ans=max(ans,f[t][i]);
    cout<<ans;
    return 0;
}
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值