CF每日5题Day3(1400)

菜鸡每日刷题。
写题好慢,五道题能写好几小时,以后要限时写。

1- 1863D 构造

参考思路

  • 第二个样例知道只有每行每列能涂颜色的位数能被2整除,就有解
  • 一个骨牌 一黑一白抵消 横向的骨牌可以影响纵向黑白平衡 纵向可以影响横向
  • 为了维持黑白平衡 纵向遍历安排横着的骨牌黑白,横向遍历安排纵向骨牌黑白。
char cl[2]={'W','B'};
void solve(){
    int n,m;
    cin>>n>>m;
    vector<int>r(n+1,0),c(m+1,0);
    vector<vector<char>>a(n+1,vector<char>(m+1));
    vector<vector<int>>ans(n+1,vector<int>(m+1,-1));    
    forr(i,1,n){
        string s;cin>>s;
        forr(j,1,m){
            a[i][j]=s[j-1];
            if(s[j-1]!='.')c[j]++,r[i]++;
        }
    }
    forr(i,1,n)if(r[i]%2!=0)return cout<<-1<<endl,void();
    forr(i,1,m)if(c[i]%2!=0)return cout<<-1<<endl,void();
    
    forr(i,1,n){
        int x=0;
        forr(j,1,m){//横向遍历
            if(a[i][j]=='U'){//遍历到一个骨牌的上部
                ans[i][j]=x;
                ans[i+1][j]=ans[i][j]^1;//下部由上部决定
                x=x^1;
            }
        }
    }
    forr(j,1,m){
        int x=0;
        forr(i,1,n){//纵向遍历
            if(a[i][j]=='L'){//左部
                ans[i][j]=x;
                ans[i][j+1]=ans[i][j]^1;//决定右部
                x=x^1;
            }
        }
    }
    forr(i,1,n){
        forr(j,1,m){
            if(ans[i][j]==-1)cout<<'.';
            else cout<<cl[ans[i][j]];
        }
        cout<<endl;
    }
}

2- 1408B 贪心

题意:

  • a i a_i ai分解成 m m m个数 放在数组 b b b相应的位置 b j , i b_{j,i} bj,i a i = ∑ j = 1 m b j , i a_i=\sum_{j=1}^m b_{j,i} ai=j=1mbj,i
  • 限制 b j b_j bj中有 k k k种数字

思路

  • 就像收麦子 从前往后收 a i a_i ai 一次收 k k k种 算到后面收了几次
void solve(){
    int n,k;
    cin>>n>>k;
    vector<int>a(n+1);
    set<int>s;
    forr(i,1,n){
        cin>>a[i];
        s.insert(a[i]);
    }
    if(k==1){//特判
        if(s.size()>1)return cout<<-1<<endl,void();
        else return cout<<1<<endl,void();
    }else if(k>1){//可以是多种数字
        s.clear();
        int ans=0,fg=1,i=1;
        while(i<=n){
            int now=a[i];
            s.insert(now);
            while (i<=n&&a[i]==now)i++;//消除重复数字 (也可以用unique处理数组
            if((s.size()==k)||(i>n)){//到最后位置(i>n) 没满k个也放进答案
                s.clear();
                s.insert(0);//前面的数收割完了 之后的b这个位置就是0 占一个种类
                ans++;
            }
        }
        // if(!s.empty()&&s.size()<=(fg==1?k-1:k)){
        //     // cout<
        //     s.clear();
        //     fg=1;
        //     ans++;
        // }
        cout<<ans<<endl;
    }
}

3- 1200C gcd 数论

  • 两位置之间有从内到外贯通的墙 就没办法通行
  • 设整个圆长 k ( k 可以看作整圆角度 360 ° ) k(k可以看作整圆角度360°) k(k可以看作整圆角度360°) 内墙 k n k\over n nk 外墙 k m k\over m mk 贯通的墙在 k gcd ⁡ ( n , m ) k\over \gcd(n,m) gcd(n,m)k位置 就是把长度 k k k分成了 gcd ⁡ ( n , m ) \gcd(n,m) gcd(n,m)
  • 如果 gcd ⁡ ( n , m ) = 1 \gcd(n,m)=1 gcd(n,m)=1,那么都可以贯通
void solve(){
    int n,m,q;//n内部 m外部
    cin>>n>>m>>q;
    //一开始非得扯上角度 麻烦了 其实角度可以消掉
    // int g=__gcd(360/n,360/m);
    // // cout<
    // int lcm=360*360/(n*m*g);
    // if(lcm>=360)fg=1;
    // int ig=lcm*n/360,og=lcm*m/360;
    int g=__gcd(n,m);//分几组
    int ig=n/g,og=m/g;//一组几个
    forr(i,1,q){
        int sx,sy,ex,ey;
        cin>>sx>>sy>>ex>>ey;
        if(g==1){
            cout<<"YES"<<endl;
            continue;
        }
        int scnt=(sy-1)/(sx==1?ig:og),ecnt=(ey-1)/(ex==1?ig:og);
        if(scnt==ecnt)cout<<"YES"<<endl;
        else cout<<"NO"<<endl;
    }
}

4- 1750C 位运算 找规律 构造

  • 看懂操作:
    • 最后目标是让 a 、 b a、b ab都为0
    • 操作可逆 逆着想 a 、 b a、b ab都为0时进行操作后,每一位 a i ⊕ b i = 1 a_i\oplus b_i=1 aibi=1都相异
    • 再操作 a a a数组 [ l , r ] [l,r] [l,r]全取反,就跟 b b b [ l , r ] [l,r] [l,r]区间相同;同理 b b b的取反的部分也跟 a a a相同 所以两数组都相同
    • 能实现的条件即上面加粗两条
  • 构造操作过程:
    • 相异:
      • 偶数次操作 a b ab ab相异,奇数相同,所以需要奇数次操作
    • 相同
      • 偶数次相同,奇数次相异
    • a a a变成0000…,现在 b b b是111…,然后一顿操作
void solve(){
    int n;
    cin>>n;
    string a,b;
    cin>>a>>b;
    //相同 相异
    int xor0=1,xor1=1;
    forr(i,0,n-1){
        if(a[i]==b[i])xor1=0;//不是相异
        else xor0=0;
    }
    // cout<
    //判断
    if(!xor0&&!xor1)return cout<<"NO"<<endl,void();
    cout<<"YES"<<endl;
    //开始构造
    int ans=0;vector<pair<int,int>>res;
    if(xor1){//相异
        forr(i,0,n-1){
            if(a[i]=='1'){
                ans++;
                res.push_back({i+1,i+1});
            }
        }
        if(ans%2==0){//相异
            res.push_back({1,1});
            res.push_back({2,n});
            res.push_back({1,n});
        }
    }
    else if(xor0){//相同
        forr(i,0,n-1){
            if(a[i]=='1'){
                ans++;
                res.push_back({i+1,i+1});
            }
        }
        if(ans%2){//现在相异
            res.push_back({1,1});
            res.push_back({2,n});
            res.push_back({1,n});
        }
    }
    cout<<res.size()<<endl;
    for(auto x:res)cout<<x.first<<' '<<x.second<<endl;
}

感觉是很简单的找规律题,但是对我这种菜鸡来说,想对思路需要耗费很长时间。

5- 1883F

参考思路

  • 子串不是连续选

实际上,如果一个子串满足条件,那么一个必要条件就是子串左侧没有与该子串左端一样的数字并且子串右侧也没有与该子串右端一样的数字
可以发现,这个条件也是子串符合题意得充分条件。
如果满足这个条件,那么左侧一定没有第二种选法可以获得这个子串的前缀,同理右侧也无法获得这个子串的后缀,所以一定没有第二种选法可以获得这个字串。
所以,我们可以根据这个充要条件去找答案。

void solve(){
    map<int,int>cnt;
    int n;cin>>n;
    vector<int>a(n+1),v(n+1,0);//v记录从前往后 有几个第一次出现的数字
    forr(i,1,n)cin>>a[i];
    forr(i,1,n){
        if(cnt.count(a[i])==0){//从前往后 第一次出现
            v[i]=v[i-1]+1;
            cnt[a[i]]=1;
        }else{
            v[i]=v[i-1];
        }
    }
    // forr(i,1,n)cout<
    cnt.clear();
    int k=0;
    reforr(i,1,n){
        if(cnt.count(a[i])==0){//从后往前 第一次出现
            k+=v[i];
            cnt[a[i]]=1;
        }
    }
    cout<<k<<endl;
}

你可能感兴趣的:(codeforce练习,算法,数据结构)