A 打靶

首先是如果 已经大于 了,那显然不行。如果 小于等于 ,则考虑剩余天数 是否大于等于缺少的分数

void sol(){
    int n,m,x,y;cin>>n>>m>>x>>y;
    cout<<(n-m>=y-x&&x<=y?"Yes":"No")<<endl;
}

B 小蓝的疑惑

根据唯一分解定理 ,

,

那么要求 最小, 就只能是 了,此时 就是

void sol(){
    int x,y;cin>>x>>y;
    if(y%x!=0) return cout<<-1<<endl,void();
    cout<<x<<" "<<y<<endl;
}

C k级序列

根据题意可以得到,每一个 范围为 , 所以我们只需要让每一个 在可以取的范围中取最小值,这样后续的 可选的范围才会最大。

void sol(){
    int n,k;cin>>n>>k;
    vector<ll> a(n+1),b(n+1);
    b[0]=-1e18;

    rep(i,1,n) cin>>a[i];

    rep(i,1,n) {
        ll l=max(b[i-1],a[i]-k),r=a[i]+k;
        if(l>r) return cout<<"No"<<endl,void();
        b[i]=l;
    }

    cout<<"Yes"<<endl;
}

D Reverse

打比赛时有点脑淤血,居然把题目看成了每次询问最长的 串长度了,等发现读错题时代码都要写完了。

分类讨论一下 :

  • 那么区间两端如果有 串被破坏,也能立刻跟翻转过来的 接上,形成新的 串,所以数量是不会变的
  • 那么区间两端不会有 串被破坏,所以数量不会变
  • 如果 端有 串被破坏,那么数量会加一,而如果 端之外恰好有 那么会跟翻转过来的 串连接,此时数量减一。
  • 分析同上一情况
void sol(){

    int n,q;cin>>n>>q;
    string s;cin>>s;
    s+='0';

    int cnt=0;
    rep(i,1,n) if(s[i]=='0'&&s[i-1]=='1') cnt++;


    while(q--) {
        int L,R;cin>>L>>R;
        L--,R--;
        if(s[L]==s[R]) 
            cout<<cnt<<endl;
        else if(s[L]=='0') {
            int ans=cnt;
            if(L>0&&s[L-1]=='1') ans--;
            if(R<n-1&&s[R+1]=='1') ans++;
            cout<<ans<<endl;
        }
        else if(s[R]=='0') {
            int ans=cnt;
            if(L>0&&s[L-1]=='1') ans++;
            if(R<n-1&&s[R+1]=='1') ans--;
            cout<<ans<<endl;
        }
    }
}

E Dog vs Cat

分类讨论一下:

  • 如果 ,那么当 时 Dog 必胜。而 时根据 的奇偶,如果是奇数的话,就会是 Cat 最终把 变成 ,此时 Cat 必输 ,反之 Dog 必输。
  • 如果 , 如果 那么除非差值等于 ,这样 Cat 可以先手将两个数变为相等,此时 Cat 必胜,否则 Cat 将无法操作,Dog 必胜。 需要注意的是当两个数分别为 时, Cat 也无法操作。
  • 如果 :
    • 的个数 已经满足 。此时 Cat 无法操作,所以 Dog 必胜。
    • ,那局面会最终变成 ,剩余都为 ,此时操作的人无法操作,另一个人必胜,判断到达此局面的奇偶即可。
void sol(){
    int n;cin>>n;

    vector<ll> a(n+1);
    rep(i,1,n) cin>>a[i];

    if(n==1) {
        if(a[1]==0) cout<<"Dog"<<endl;
        else cout<<(a[1]%2==1?"Dog":"Cat")<<endl;
    }
    else if(n==2) {
        if(abs(a[1]-a[2])!=1||max(a[1],a[2])==1) cout<<"Dog"<<endl;
        else cout<<"Cat"<<endl;
    }
    else {
        int cnt=count(all(a),0)-1;
        ll sum=-n+((n+1)/2-1);//减去 1 的数量
        for(int x:a) sum+=x;

        if(cnt*2>=n) cout<<"Dog"<<endl;
        else cout<<(sum%2==1?"Dog":"Cat")<<endl;
    }
}

F 小蓝的构造

对于数据只有 的题目,我们可以考虑暴搜或者考虑二进制枚举。但是直接枚举的话 的解空间又太大了,所以我们需要根据题目中的约束条件进行剪枝,缩小解空间。

若我们只枚举前一半的字符串,那么在已经确定了一半字符的情况下根据 就可以确定最后一个字符,此时再根据 就能确定倒数第二个字符。以此类推,我们就会发现前一半的字符串就已经决定了后一半字符串,而枚举前一半字符串的计算量仅为 确定后一半字符串的复杂度为 ,算一算最大也只有约为 ,此时已经非常可做了。

void sol(){
    int n;cin>>n;
    vector<int> f(n);
    rep(i,1,n-1) cin>>f[i];
    
    string s;
    auto check=[&](int fl)->bool {
        s=string(n,'0');
        rep(i,0,(n-1)/2+1) if(fl>>i & 1) s[i]='1';

        per(len,n-1,1) {
            int cnt=0;
            for(int i=0,j=len;j<n;i++,j++) 
                if(s[i]=='0'&&s[j]=='1') cnt++;       

            if(cnt!=f[len]) {
                if(len>=(n-1)/2+1&&cnt==f[len]-1&&s[0]=='0') s[len]='1';
                else return false;
            }
        }

        return true;
    };

    for(int i=0;i<(1<<((n-1)/2+1));i++) 
        if(check(i)) return cout<<s<<endl,void();

    cout<<-1<<endl;
}