Codeforces Round 935 (Div. 3)(A~E)

A. Setting up Camp

a,必须要一个人住。

b,必须要三个人住。

c,无所谓(1~3人)

问最少要几个帐篷。

a一定要一个人住,所以最少a个帐篷。

b要三个人住,如果b不是三的倍数,就从c那边拿人过来,帐篷再加b/3

如果c是负数,则无法完成分配,输出负一。

不是负数就把c全部安排三人间,有余数再加1。

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;

void solve(){
    int a,b,c;
    cin>>a>>b>>c;

    int ans=a;

    while(b%3!=0){
        b++;
        c--;
    }
    if(c<0){
        cout<<-1<<endl;
    }else{
        ans+=b/3;
        ans+=c/3;
        if(c%3)ans++;
        cout<<ans<<endl;
    }

}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--)solve();
    return 0;
}

B. Fireworks

a烟花启动之后,每隔a分钟都会发射一次,持续m时间,即 [a,a+m],都有烟花。

b烟花启动之后,每隔b分钟都会发射一次,持续m时间,即 [b,b+m],都有烟花。

a,b烟花同时发射,同一时间最多看到几个烟花。

因为是同时发射的,所以定死了区间是 [1,m],只需要考虑 m 里面 a 和 b 能发射几次即可。

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;

void solve(){
    int a,b,m;
    cin>>a>>b>>m;

    cout<<m/a+1+m/b+1<<endl;
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--)solve();
    return 0;
}

C. Left and Right Houses

每个房子为0或者1,0011111这样就是一排房子,你需要在其中修一条路,放在任意的房子 i 之后。假设路修在 i 之后,需要满足条件:

1、左侧至少有 i/2 个0

2、右侧至少有 (n-i)/2 个1(两个条件除法均向上取整)

所有合法的位置 i,输出使得 |n/2 - i| 最小的位置。

对于任意的位置,左侧的0的数量用前缀和来维护,右侧的1用后缀和来维护,就可以O(1)访问。

满足情况的位置全部存下来,最后再对 |n/2 - i| 进行判断。(注意这里不是整除,n/2要用double类型,否则会产生错误)

#include <bits/stdc++.h>
//#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;

void solve(){
    int n;
    cin>>n;

    string s;
    cin>>s;

    s="0"+s;

    int len=s.length()-1;

    int pre[len+5],bac[len+5];
    per(i,1,len)pre[i]=bac[i]=0;

    //左侧[0,i]
    //右侧[i+1,len]

    if(s[1]=='0')pre[1]=1;
    per(i,2,len){
        pre[i]=pre[i-1];
        if(s[i]=='0')pre[i]++;
    }

    if(s[len]=='1')bac[len]=1;
    rep(i,len-1,1){
        bac[i]=bac[i+1];
        if(s[i]=='1')bac[i]++;
    }

    int ans=INT_MAX,idx=INT_MAX;

    pre[0]=0;
    bac[0]=bac[1];

    bac[len+1]=0;

    vector<int>v;

    per(i,0,len){
        if(pre[i]>=i/2+i%2 and bac[i+1]>=(n-i)/2+(n-i)%2){
            v.push_back(i);
        }
    }

    double cul=abs((double)n/2-v[0]);
    int ansidx=v[0];
    per(i,1,v.size()-1){
        if(abs((double)n/2-v[i])<cul){
            cul=abs((double)n/2-v[i]);
            ansidx=v[i];
        }
    }
    cout<<ansidx<<endl;
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--)solve();
    return 0;
}

D. Seraphim the Owl

1 2 3 4 5 6 7 8 9 10 n n+1

现在主角站在 n+1 的位置,他需要插队到前 m 的位置。

设主角当前在 i,他可以选择任意一个 j<i,支付 a[j] 来交换位置;同时给所有的 j<k<i,支付 b[k](就是夹在他们俩中间的所有人)。

问主角插队到前m,最少要花多少钱。

1 2 3 4 5 6 7 8 9 10 n n+1

首先模拟几下,主角从 n+1,到 n,只有一种方法;但是从 n+1 到 10,可以交换 n,在交换到10,或者直接从 n+1 交换到10,并且再付出 b[10] 的代价。这样看显然就是一道dp了。

定义 dp[i] 为到达 i 位置需要的最少花费

第一种:从 n 直接跳过来。

即:dp[i]=a[i]+\sum_{j=i+1}^nb[j]

第二种:从后面的 x ∈(i+1 ~ n)最少花费转移过来。

即:dp[i]=a[i]+\sum_{j=i+1}^{x-1}b[j]+dp[x] 

显然每次转移,b 数组的花费都是一串连续的和,所以可以对 b 数组求一次前缀和加快访问。

将式子优化为:

① dp[i]=a[i]+pre[n]-pre[i]

② dp[i]=a[i]+pre[x-1]-pre[i]+dp[x],x[i+1,n]

得出代码

 dp[n]=a[n];
    rep(i,n-1,1){
        dp[i]=a[i]+pre[n]-pre[i];
 
        per(x,i+1,n){
           dp[i]=min(dp[i],a[i]+pre[x-1]-pre[i]+dp[x]);
        }

    }

但是题目中 n=2e5,n*n的复杂度过不了,外层循环已经不能优化了,考虑降低内层循环的复杂度。

观察式子,我们把常量提出来。

dp[i]=min(dp[i],a[i]+pre[x-1]-pre[i]+dp[x]);

->

dp[i]=min(dp[i],a[i]-pre[i]+min(pre[x-1]+dp[x]));

发现pre[x-1]和dp[x]均要后面的最小值,所以用变量来存pre[x-1]+dp[x]的最小值即可。

#include <bits/stdc++.h>
#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;

void solve(){
    int n,m;
    cin>>n>>m;

    int a[n+1],b[n+1];
    per(i,1,n)cin>>a[i];
    per(i,1,n)cin>>b[i];

    int pre[n+1];
    per(i,1,n){
        if(i==1)pre[i]=b[i];
        else pre[i]=pre[i-1]+b[i];
    }

    int dp[n+1];

    int MIN=LONG_LONG_MAX;

    dp[n]=a[n];
    rep(i,n-1,1){

        dp[i]=a[i]+pre[n]-pre[i];

        int BASE=a[i]-pre[i];
        MIN=min(dp[i+1]+pre[i],MIN);

        dp[i]=min(dp[i],BASE+MIN);
    }

    int mincost=LONG_LONG_MAX;
    per(i,1,m)mincost=min(mincost,dp[i]);

    cout<<mincost<<endl;
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--)solve();
    return 0;
}

补题:E. Binary Search

给你一个排列,对他进行二分查找,要求最终 p[l]=x。

可以在查找前进行操作(最多2次),交换任意下标的元素。

观察二分退出的条件是r=l+1

也就是 l-----------> l r <-------------r,从外围到里面,然后退出了。

假设他们走过了一些m

l--m1----m2---> l r <---m3----m4----r

一定有:m1,m2<=x;m3,m4>x

因为最终 x 要在 l 的位置,所以我们考虑能否直接将 x 挪到 l 上。

如果 x 不在 m1,m2,m3,m4上,显然不会对二分产生影响,直接交换 l 和 x 的位置。

如果 x 在 m1,m2 上,首先还是需要交换 x 和 l,若 p[l]<=x 无影响,可以随意交换。

若 p[l]>x 则交换后会对二分查找产生影响,但是观察代码 p[m]<=x,才会有 l=m,所以 l 的落点和经过的位置,一定有 p[m]<=x,即此情况不存在,直接交换无影响。

再考虑 x 在 m3,m4上,因为 r 要往左走,一定有 p[m]>x,所以 x 不会在 m3 和 m4 上。

故总结,如果二分查找结束,p[l]!=x,我们只需要交换一次 l 和 x。

#include <bits/stdc++.h>
//#define int long long
#define per(i,j,k) for(int (i)=(j);(i)<=(k);++(i))
#define rep(i,j,k) for(int (i)=(j);(i)>=(k);--(i))
#define debug(a) cout<<#a<<"="<<a<<endl
#define all(x) x.begin(),x.end()
#define EX exit(0)
#define fr first
#define se second
#define endl '\n'
using namespace std;
using ll=long long;

void solve(){
    int n,x,xIDX;
    cin>>n>>x;

    int p[n+1];
    per(i,1,n){
        cin>>p[i];
        if(p[i]==x)xIDX=i;
    }

    int l=1,r=n+1;
    while(r-l!=1){
        int m=(r+l)>>1;
        if(p[m]<=x)l=m;
        else r=m;
    }

    if(p[l]==x){
        cout<<0<<endl;
    }else{
        cout<<1<<endl;
        cout<<xIDX<<" "<<l<<endl;
    }
}

signed main(){
    ios::sync_with_stdio(false),cin.tie(nullptr);
    int t=1;
    cin>>t;
    while(t--)solve();
    return 0;
}

  • 24
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值