Educational Codeforces Round 101前四道

Educational Codeforces Round 101 (Rated for Div. 2)

由于题目都是英文的,贴在这里也没有什么意义,就不贴了。

还是这里只贴出前四道简单的题的思路。

A. Regular Bracket Sequence

T组( T∈[1,1000])数据,每组数据:给出只包含一个“( ”和一个“ )”,其它位置都是“ ?”的长度大于等于2的字符串S。求能否填充“ ?” 为“ )”或“( ”使得S串成为一个好的括号序列。

思路是判断S第一位不是“)”,最后一位不是“(”,且S长度为偶数。时间复杂度O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

string f(string s){
    return s.size()%2==0&&s[0]!=')'&&s[s.size()-1]!='('?"YES":"NO";  
}

int main(){
    int t;
    cin>>t;
    while(t--){
        string s;
        cin>>s;
        cout<<f(s)<<endl;
    }
    
    
    return 0;
}

B. Red and Blue

T组( T∈[1,1000] )数据,每组给你一个长度为n的序列和一个长度为m的序列(n,m∈[1,100]),这两个序列要组合成为一个长度为n+m的新序列,但每个数字在原序列的相对顺序不能改变,有 2 n + m 2^{n+m} 2n+m种组合方式,求这些组合方式中,序列前缀和的最大值。

思路是把这两个序列的前缀和最大值相加,因为我们求的就是这两个序列的前缀和的最大值,也就是分别求出这两个序列的前缀和最大值。时间复杂度O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;

int main(){
    int t;
    cin>>t;
    while(t--){
        int n,m;
        cin>>n;
        int u=0,tot=0;
        for(int i=0;i<n;i++){
            int x;
            cin>>x;
            tot+=x;
            u=max(u,tot);
        }
        cin>>m;
        int v=0;
        tot=0;
        for(int i=0;i<m;i++){
            int x;
            cin>>x;
            tot+=x;
            v=max(v,tot);
        }
        cout<<u+v<<endl;
    }
    
    
    return 0;
}

C. Building a Fence

T组( T∈[1,10000] )数据,每组给你一个长度为n (n∈[2,2e5])的数组和一个数字k(k∈[2,1e8]),代表平地的高度和积木的固定高度,问能否按照题目规则摆放完所有的n个积木。

思路是递推,知道第i个积木的放置范围,就能推出第i+1个积木的放置范围。时间复杂度O(n)

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010;
typedef long long LL;
LL a[N];
int n;
LL k;

bool ck(){
    LL l=a[1]+1,r=a[1]+1;
    for(int i=2;i<n;i++){
        LL ll=a[i]+1,rr=a[i]+k;
        if(ll>r+k-1||rr+k-1<l){
            return 0;
        }
        l=max(ll,l-k+1);
        r=min(rr,r+k-1);
        // cout<<l<<' '<<r<<endl;
    }
    if(l>a[n]+k||r+k-1<a[n]+1){
        return 0;
    }
    return 1;
}

int main(){
    int t;
    cin>>t;
    while(t--){
        cin>>n>>k;
        for(int i=1;i<=n;i++){
            cin>>a[i];
        }
        if(ck()){
            cout<<"YES"<<endl;
        }else{
            cout<<"NO"<<endl;
        }
    }
    
    
    return 0;
}

D. Ceil Divisions

T组( T∈[1,1000] )数据,每组给你一个数n( n∈[3,2e5]),问将序列 [1,2,3,…,n]通过任意位置间除法上取整运算得到序列[1,2,1,1,1,1…,1]需要多少步。

题目要求是步数不能大于n+5。

最简单的方法就是3/4,4/5,5/6,… ,最后用n一直除以2,但这需要n+logn步,n大于1000就不符合要求了。

于是想到利用倍数差,先把[1,2,3,…,n]转化为[1,2,1,4,1,1,1,8,…,16,…,32,…,1024,…,n],再从后往前把这几个没有变成1的数用两步变成1。。。。这也需要n+logn步。。。。。

于是把倍数差变大,经计算变成8可以卡过n+5,因为n等于2e5的时候logn小于18,则 l o g 8 n log_8n log8n<=6,最多多出7步(8的倍数那6步加上n多出的1步),然而变[1,2,3,…,n]为[1,2,1,1,1,1,1,8,…,64,…,512,…,n]需要n-2-7步,(n-2-7)+7*2=n+5,正巧卡过。(相同代码可以合并,奈何智商没有那么高,当时有心无力,现在也不想改了。)

还有一种方法,是标准解法。只看n,用(i-1)/i,遇到根号n就用n除以根号n。最后3到n-1都一次变到1,n用不到5次变到1。

这题考数论,n变到2的最快方法是开根号。

#include<iostream>
#include<cstdio>
#include<cstring>
using namespace std;
const int N=200010;
typedef long long LL;

void show(int n){
    if(n==8){
        cout<<5<<' '<<6<<endl;
        cout<<6<<' '<<7<<endl;
        cout<<7<<' '<<8<<endl;
        cout<<8<<' '<<4<<endl;
        cout<<8<<' '<<2<<endl;
        cout<<3<<' '<<4<<endl;
        cout<<4<<' '<<2<<endl;
        cout<<4<<' '<<2<<endl;
        return;
    }
    for(int i=n/8+1;i<=n-1;i++){
        cout<<i<<' '<<i+1<<endl;
    }
    cout<<n<<' '<<n/8<<endl;
    cout<<n<<' '<<8<<endl;
    show(n/8);
}

void solve(int n){
    if(n<8){
        cout<<(n-2)*2<<endl;
        for(int i=n;i>2;i--){
            cout<<i<<' '<<i-1<<endl;
            cout<<i<<' '<<2<<endl;
        }
        return;
    }else if(n==8){
        cout<<8<<endl;
        show(8);
        return;
    }
    
    int x=64,cnt=0;
    while(x<n){
        cnt++;
        x*=8;
    }
    cout<<n+cnt+1<<endl;
    if(n>64*8*8*8){
        for(int i=64*8*8*8+1;i<=n-1;i++){
            cout<<i<<' '<<i+1<<endl;
        }
        cout<<n<<' '<<64*8*8*8<<endl;
        cout<<n<<' '<<8<<endl;
        show(64*8*8*8);
    }else if(n>64*8*8){
        for(int i=64*8*8+1;i<=n-1;i++){
            cout<<i<<' '<<i+1<<endl;
        }
        cout<<n<<' '<<64*8*8<<endl;
        cout<<n<<' '<<8<<endl;
        show(64*8*8);
    }else if(n>64*8){
        for(int i=64*8+1;i<=n-1;i++){
            cout<<i<<' '<<i+1<<endl;
        }
        cout<<n<<' '<<64*8<<endl;
        cout<<n<<' '<<8<<endl;
        show(64*8);
    }else if(n>64){
        for(int i=64+1;i<=n-1;i++){
            cout<<i<<' '<<i+1<<endl;
        }
        cout<<n<<' '<<64<<endl;
        cout<<n<<' '<<8<<endl;
        show(64);
    }else if(n>8){
        for(int i=8+1;i<=n-1;i++){
            cout<<i<<' '<<i+1<<endl;
        }
        cout<<n<<' '<<8<<endl;
        cout<<n<<' '<<8<<endl;
        show(8);
    }
    
}

int main(){
    int t;
    cin>>t;
    while(t--){
        int n;
        cin>>n;
        solve(n);
    }
    
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值