Codeforces Round #608 (Div. 2)

场次链接

A. Suits
题目链接
给你a条领带,b条围巾,c件背心,d件夹克。一条领带和一件夹克可以组成第一类西装,价值为e。一条围巾和一件背心和一件夹克可以组成第二类西装,价值为f。问最多能组成多少价值。
数据范围: 1 ≤ a ≤ 1 0 5 1\leq a\leq 10^5 1a105, 1 ≤ b ≤ 1 0 5 1\leq b\leq 10^5 1b105, 1 ≤ c ≤ 1 0 5 1\leq c\leq 10^5 1c105, 1 ≤ d ≤ 1 0 5 1\leq d\leq 10^5 1d105, 1 ≤ e ≤ 1 0 3 1\leq e\leq 10^3 1e103, 1 ≤ f ≤ 1 0 3 1\leq f\leq 10^3 1f103
解:两种西装都需要一件夹克,所以夹克先给价值高的用,如果多了再给价值低的用。
复杂度: O ( 1 ) O(1) O(1)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
    int a,b,c,d,e,f;
    scanf("%d%d%d%d%d%d",&a,&b,&c,&d,&e,&f);
    int ans=0;
    if(e>f){
        ans+=min(a,d)*e;
        d-=min(a,d);
        int k=b;
        k=min(k,c);
        k=min(k,d);
        ans+=k*f;
    }else{
        int k=b;
        k=min(k,c);
        k=min(k,d);
        d-=k;
        ans+=k*f;
        ans+=min(a,d)*e;
    }
    printf("%d\n",ans);
}
int main()
{
    //ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    T=1;
    while(T--){
        work();
    }
}

B. Blocks
题目链接
给你一个长度为n的字符串,只包含B和W两种字符,你可以选择两个相邻的字符,使这两个字符B变W,W变B,问能否在3*n的操作内使所有字符相同,不能输出-1,能的话要求输出操作的数量即位置( 值代表操作这个位置和他右边这个位置)。
数据范围: 2 ≤ n ≤ 200 2\leq n \leq 200 2n200
解:显然,每一次操作会使两种字符的数量变化偶数个。所以只要判断2种字符的数量是否是偶数即可,然后暴力找操作位置。
复杂度: O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
char a[205];
void work()
{
    int n;
    scanf("%d",&n);
    scanf("%s",a);
    int x=0;
    int y=0;
    for(int i=0;i<strlen(a);i++){
        if(a[i]=='B'){
            x++;
        }else{
            y++;
        }
    }
    if(x%2==1&&y%2==1){
        printf("-1\n");
        return;
    }
    vector<int>v;
    if(x%2==0){
        for(int i=0;i<strlen(a)-1;i++){
            if(a[i]=='B'){
                v.push_back(i);
                a[i]='W';
                if(a[i+1]=='B'){
                    a[i+1]='W';
                }else{
                    a[i+1]='B';
                }
            }
        }
    }else{
        for(int i=0;i<strlen(a)-1;i++){
            if(a[i]=='W'){
                v.push_back(i);
                a[i]='B';
                if(a[i+1]=='B'){
                    a[i+1]='W';
                }else{
                    a[i+1]='B';
                }
            }
        }
    }
    printf("%d\n",v.size());
    for(int i=0;i<v.size();i++){
        printf("%d ",v[i]+1);
    }
    printf("\n");
}
int main()
{
    //ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    T=1;
    while(T--){
        work();
    }
}

C. Shawarma Tent
题目链接
给一个矩形网格,给出学校位置sx,sy,然后给出n个学生的家x,y。你要在一个地方修一个帐篷,如果有学生到学校的任意最短路径会经过帐篷,则值+1,输出最大值,并输出帐篷位置。
数据范围: 1 ≤ n ≤ 2 ∗ 1 0 5 1\leq n \leq 2*10^5 1n2105, 0 ≤ s x , s y ≤ 1 0 9 0\leq s_x,s_y \leq 10^9 0sx,sy109, 0 ≤ x i , y i ≤ 1 0 9 0\leq x_i,y_i \leq 10^9 0xi,yi109
解:显然答案会在 ( s x + 1 , s y ) (sx+1,sy) (sx+1,sy) ( s x − 1 , s y ) (sx-1,sy) (sx1,sy) ( s x , s y + 1 ) (sx,sy+1) (sx,sy+1) ( s x , s y − 1 ) (sx,sy-1) (sx,sy1)这四个值中取到。那么枚举每一个学生和学校的相对位置,判断可以经过哪几个点,最后取最大值即可。
复杂度 O ( n ) O(n) O(n)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
void work()
{
    int n,sx,sy;
    int a=0,b=0,c=0,d=0;
    scanf("%d%d%d",&n,&sx,&sy);
    for(int i=1;i<=n;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        if(x<sx){
            if(y<sy){
                a++;
                b++;
            }
            if(y>sy){
                b++;
                c++;
            }
            if(y==sy){
                b++;
            }
        }
        if(x==sx){
            if(y<sy){
                a++;
            }else{
                c++;
            }
        }
        if(x>sx){
            if(y<sy){
                a++;
                d++;
            }
            if(y==sy){
                d++;
            }
            if(y>sy){
                c++;
                d++;
            }
        }
    }
    int maxx=a;
    maxx=max(maxx,max(b,max(c,d)));
    printf("%d\n",maxx);
    if(maxx==a){
        printf("%d %d\n",sx,sy-1);
    }else if(maxx==b){
        printf("%d %d\n",sx-1,sy);
    }else if(maxx==c){
        printf("%d %d\n",sx,sy+1);
    }else printf("%d %d\n",sx+1,sy);
}
int main()
{
    //ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    T=1;
    while(T--){
        work();
    }
}

D. Portals
题目链接
有n座城堡,每座城堡需要 a i a_i ai个人才能进入,进入城后可以招募到 b i b_i bi个人,如果城堡中驻扎有人,那么可以获得 c i c_i ci的贡献。给出m条边,连着2个城堡 u , v , u > v u,v,u>v u,v,u>v,你可以在到达城堡时派一个人驻扎在该城,或者在到达u城时派一个人到v城驻扎。(不能连着通过2条边)你必须按顺序进入n个城,并且必须到达第n个城并进入。初始你拥有k个人,问你能获得的最大贡献。
输入 n,m,k,然后n个城堡的 a i , b i , c i a_i,b_i,c_i ai,bi,ci,然后m条边 u i , v i u_i,v_i ui,vi
数据范围 1 ≤ n ≤ 5000 1\leq n\leq 5000 1n5000, 1 ≤ m ≤ m i n ( n ∗ ( n − 1 ) 2 , 3 ∗ 1 0 5 ) 1\leq m\leq min(\frac{n*(n-1)}{2},3*10^5) 1mmin(2n(n1),3105), 0 ≤ k ≤ 5000 0\leq k\leq 5000 0k5000, 0 ≤ a i , b i , c i ≤ 5000 0\leq a_i,b_i,c_i\leq 5000 0ai,bi,ci5000, 1 ≤ v i ≤ u i ≤ n 1\leq v_i\leq u_i\leq n 1viuin, k + ∑ i = 1 n b i ≤ 5000 k+\sum_{i=1}^{n}b_i\leq 5000 k+i=1nbi5000
解:(辣鸡题意,没发样例解释前完全没看懂)
对于每一个城来说,从越靠近n的城派人过去越优,故首先开一个数组代表这个城可以最迟从哪个城派人过来,初始化为 i i i,然后遍历所有边,把值更新至最大,即该城最迟可以在哪个地方派人过去。
我们可以很简单的求出到达每一个城的人数,再减去下个城进入所需要的人数,那么我们就可以求出在每个城可以派出去的人数。以每个城可以派出去的人数建一棵线段树,维护最小值,然后将城堡按照贡献从大到小排序,遍历 i i i,从该城最迟派人的地方到n区间-1,然后判断 0 ≤ m i n 0\leq min 0min,如果成立,则加入贡献,否则将1加回。
复杂度 O ( n ∗ log ⁡ n ) O(n*\log n) O(nlogn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node
{
    int id;
    int a;
    int b;
    int c;
}num[5005];
int ans[5005];
int vis[5005];
bool cmp(node x,node y)
{
    if(x.c!=y.c)return x.c>y.c;
    else return vis[x.id]>vis[y.id];
}
int tr[5005<<2];
int lazy[5005<<2];
void build(int l,int r,int rt)
{
    if(l==r){
        tr[rt]=ans[l];
        //printf("%d %d\n",l,tr[l]);
        return;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    tr[rt]=min(tr[rt<<1],tr[rt<<1|1]);
}
void pushdown(int rt)
{
    if(lazy[rt]){
        tr[rt<<1]+=lazy[rt];
        tr[rt<<1|1]+=lazy[rt];
        lazy[rt<<1]+=lazy[rt];
        lazy[rt<<1|1]+=lazy[rt];
        lazy[rt]=0;
    }
}
void update(int l,int r,int rt,int L,int R,int C)
{
    if(L<=l&&R>=r){
        tr[rt]+=C;
        lazy[rt]+=C;
        return;
    }
    pushdown(rt);
    int m=(l+r)>>1;
    if(L<=m)update(l,m,rt<<1,L,R,C);
    if(R>m)update(m+1,r,rt<<1|1,L,R,C);
    tr[rt]=min(tr[rt<<1],tr[rt<<1|1]);
}
int query(int l,int r,int rt,int L,int R)
{
    if(L<=l&&R>=r){
        return tr[rt];
    }
    pushdown(rt);
    int ans=99999;
    int m=(l+r)>>1;
    if(L<=m)ans=min(ans,query(l,m,rt<<1,L,R));
    if(R>m)ans=min(ans,query(m+1,r,rt<<1|1,L,R));
    return ans;
}
void work()
{
    int n,m,k;
    scanf("%d%d%d",&n,&m,&k);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&num[i].a,&num[i].b,&num[i].c);
        num[i].id=i;
        vis[i]=i;
    }
    for(int i=1;i<=m;i++){
        int x,y;
        scanf("%d%d",&x,&y);
        vis[y]=max(vis[y],x);
    }
    ans[0]=k;
    int summ=k;
    num[n+1].a=0;
    for(int i=1;i<=n;i++){
        if(summ<num[i].a){
            printf("-1\n");
            return;
        }
        summ+=num[i].b;
        ans[i]=summ-num[i+1].a;
    }
    build(1,n,1);
    sort(num+1,num+n+1,cmp);
    int sum=0;
    for(int i=1;i<=n;i++){
        update(1,n,1,vis[num[i].id],n,-1);
        if(query(1,n,1,1,n)>=0){
            sum+=num[i].c;
        }else{
            update(1,n,1,vis[num[i].id],n,1);
        }
    }
    printf("%d\n",sum);
 
}
int main()
{
    //ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    T=1;
    while(T--){
        work();
    }
}

E. Common Number
题目链接
对于每个数给出一个操作:如果n为偶数,则 n = n / 2 n=n/2 n=n/2,否则 n = n − 1 n=n-1 n=n1,将得到的数放入path数组,例如 p a t h ( 32 ) = [ 32 , 16 , 8 , 4 , 2 , 1 ] path(32)=[32,16,8,4,2,1] path(32)=[32,16,8,4,2,1]
现在给你n和k,求最大的ans使得从1到n的path中含有ans的数的数量大于等于k。
数据范围 1 ≤ k ≤ n ≤ 1 0 18 1\leq k \leq n \leq 10^{18} 1kn1018
正解 对于每一个数,我们可以求出从1到n有多少个含有它,这个值在奇偶上分别满足单调性,故奇数进行二分,偶数进行二分,求最大值即可。
复杂度 O ( log ⁡ n ) O(\log n) O(logn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
ll sum(ll x)
{
    ll p2=1;
    ll ans=0;
    while(p2*x<=n){
        ll hi=min(n,p2*(x+1)-1);
        ans+=hi-p2*x+1;
        p2*=2;
    }
    return ans;
}
ll ok(ll x)
{
    if(x%2==0){
        //printf("%lld %lld %lld\n",x,sum(x)+sum(x+1));
        return sum(x)+sum(x+1);
    }else{
        //printf("%lld %lld\n",x,sum(x));
        return sum(x);
    }
}
void work()
{
    scanf("%lld%lld",&n,&k);
    ll ans=1;
    ll l=1;
    ll r=(n+1)/2;
    while(l<=r){
        ll m=(l+r)>>1;
        if(ok(m*2-1)>=k)l=m+1;
        else r=m-1;
    }
    ans=max(ans,r*2-1);
    l=1;
    r=n/2;
    while(l<=r){
        ll m=(l+r)>>1;
        if(ok(m*2)>=k)l=m+1;
        else r=m-1;
    }
    ans=max(ans,r*2);
    printf("%lld\n",ans);
}
int main()
{
    //ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    T=1;
    while(T--){
        work();
    }
}

贪心解 显然,最后的答案与n不断除二得到的值比较接近,故可以贪心暴力求解。
复杂度 O ( 20 ∗ log ⁡ n ) O(20*\log n) O(20logn)

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,k;
ll sum(ll x)
{
    ll p2=1;
    ll ans=0;
    while(p2*x<=n){
        ll hi=min(n,p2*(x+1)-1);
        ans+=hi-p2*x+1;
        p2*=2;
    }
    return ans;
}
ll ok(ll x)
{
    if(x%2==0){
        //printf("%lld %lld %lld\n",x,sum(x)+sum(x+1));
        return sum(x)+sum(x+1);
    }else{
        //printf("%lld %lld\n",x,sum(x));
        return sum(x);
    }
}
void work()
{
    scanf("%lld%lld",&n,&k);
    ll ans=1;
    ll cn=n;
    while(cn){
        for(int i=0;i<=20;i++){
            if(cn<i)break;
            if(ok(cn-i)>=k){
                ans=max(ans,cn-i);break;
            }
        }
        cn/=2;
    }
    printf("%lld\n",ans);
}
int main()
{
    //ios_base::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int T;
    //scanf("%d",&T);
    T=1;
    while(T--){
        work();
    }
}

F,不想补了。。。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值