Codeforces Round #676 (Div. 2) A~E个人题解

Dashboard - Codeforces Round #676 (Div. 2) - Codeforces

A. XORwice

题意:给你俩个数a,b,问a\bigoplus x +b\bigoplus x的最小值,x可以任意取值(\bigoplus为异或)

知识点:位运算,思维

思路:设答案为ans

我们可以先把a和b都拆成二进制后每一位考虑,

如果某一位上a和b都是1,那么x在这一位上一定是1,ans在这一位上是0

如果某一位上a和b其中一个是1,x在这一位上可以是0或者1,ans在这一位上是1

如果某一位上a和b都是0,那么x在这一位上一定是0,ans在这一位上是0

可以容易发现ans=a\bigoplus b

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =1e9+7;
void solve(){
    ll a,b;cin>>a>>b;
    cout<<(a^b)<<'\n';
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

B. Putting Bricks in the Wall

题意:给你一个n \times n的地图,地图上除了(1,1)(n,n)每个点为0或者1,现在明明想从(1,1)走到(n,n),他会在开始时随机选择1或者0来走,之后他可以在他选择的数字的格子上任意跳着走,(1,1)(n,n)是只能上下左右走一格,现在你可以修改最多2次,把0变成1,或者1变成0,问能不能让明明不管选什么数字都不能从(1,1)走到(n,n)

知识点:思维,读题(不是

思路:只要开始旁边的两个格子和结束旁边的两个格子完全不同就行了,这样不管怎么样都是不互通的,而且一定存在一个小于等于2的修改次数的答案,我们可以直接二进制枚举这4个格子的每一种状况,然后判断出来一个符合的就行了。

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =1e9+7;
char mp[205][205];
ll n;
pair<ll,ll>w[4];
char pre[4];
bool check(){
    ll cnt=0;
    for(int i=0;i<2;++i){
        ll rp=mp[w[i].first][w[i].second]-'0';
        for(int j=2;j<4;++j){
            ll wp=mp[w[j].first][w[j].second]-'0';
            if(rp^wp==1)cnt++;
        }
    }
    return cnt==4;
}
void solve(){
    cin>>n;
    for(int i=1;i<=n;++i){
        for(int j=1;j<=n;++j){
            cin>>mp[i][j];
        }
    }
    w[0]={1,2};w[1]={2,1};    //开始旁边的两个格子下标
    w[2]={n,n-1};w[3]={n-1,n};//结束旁边的两个格子下标
    pre[0]=mp[1][2];pre[1]=mp[2][1];    //开始旁边的两个格子的数字
    pre[2]=mp[n][n-1];pre[3]=mp[n-1][n];//结束旁边的两个格子的数字
    for(int i=0;i<(1<<4);++i){//二进制枚举
        for(int j=0;j<4;++j){
            if(i&(1<<j))mp[w[j].first][w[j].second]='1';
            else mp[w[j].first][w[j].second]='0';
        }
        if(check()){//如果开始旁边两个格子和结束旁边两个格子完全不同
            vector<pair<ll,ll> >ans;
            for(int j=0;j<4;++j){
                if(mp[w[j].first][w[j].second]==pre[j])continue;
                ans.push_back(w[j]);//修改了
            }
            if(ans.size()>2)continue;//修改次数不符合条件
            cout<<ans.size()<<'\n';
            for(auto [x,y]:ans)cout<<x<<' '<<y<<'\n';
            return ;
        }
    }
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

C. Palindromifier

题意:给你一个字符串s,有两个操作

设n为当前字符串长度

操作1:选择一个i,2\leq i \leq n-1,然后把s_{2.....i}反转拼接到s前面

操作2:选择一个i,2\leq i \leq n-1,然后把s_{i......n-1}反转拼接到s后面

现在你要在30次操作之内,把字符串s变成回文串

知识点:构造,思维

思路:可以发现这俩操作其实就是以左,右端点为中心,补全一个回文

 可以发现,如果我有ab,我可以凑出aba反转后是ba,我依旧可以凑出aba

所以我要把反转的字符串中的边界有一个回文中心+半径的所有字符

这样就可以把整个字符串弄成回文,具体步骤如下

 代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =1e9+7;
void solve(){
    string s;cin>>s;
    ll l=s.length();
    cout<<3<<'\n';
    cout<<'R'<<' '<<l-1<<'\n';
    cout<<'L'<<' '<<l<<'\n';
    cout<<'L'<<' '<<2<<'\n';
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

 D. Hexagons

题意:在一个二维平面上,我可以走上,下,左,右,右上,左下,6个方向走一步的代价分别为c_{1...6},现在给出一个坐标(x,y),问从(0,0)走到(x,y)的最小代价。

知识点:分类讨论,数学

思路:我们可以先讨论出上,下,左,右,左上,左下,右上,右下,8个方向走一步的代价的最小值,比如左上=min 左 + 上,左下+2*上,右上+2*左这样依次讨论出

然后对于一个4个区域来说,每个区域情况都一样,只不过代价的下标不同,我这里只给出右上半区的情况

 如果y>x,就是红(上y+右x),绿(右上x+上(y-x)),粉(右上y+左(y-x))三种情况

其他,就是棕(右上y+右(y-x)),黄绿(右上x+下(y-x)),紫(上y+右x)三种情况

剩下三个区域也是相同的

然后码代码就行了

代码:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =1e9+7;
ll c[7],dp[9];
void solve(){
    ll x,y;cin>>x>>y;
    ll tx=abs(x),ty=abs(y);
    for(int i=1;i<=6;++i)cin>>c[i];
    dp[1]=min(c[2],c[1]+c[3]);//上
    dp[2]=min(c[5],c[4]+c[6]);//下
    dp[3]=min(c[3],c[4]+c[2]);//左
    dp[4]=min(c[6],c[1]+c[5]);//右

    dp[5]=min({c[3]+c[2],c[4]+2*c[2],c[1]+2*c[3]});//左上
    dp[6]=min(c[4],c[5]+c[3]);//左下
    dp[7]=min(c[1],c[2]+c[6]);//右上
    dp[8]=min({c[5]+c[6],c[4]+2*c[6],c[1]+2*c[5]});//右下
    ll ans=2e18;//一定要比2e18大
    if(x>=0&&y>=0){//右上半区
        ans=min(ans,ty*dp[1]+tx*dp[4]);
        if(tx>ty){
            ans=min(ans,ty*dp[7]+(tx-ty)*dp[4]);
            ans=min(ans,tx*dp[7]+(tx-ty)*dp[2]);
        }
        else {
            ans=min(ans,tx*dp[7]+(ty-tx)*dp[1]);
            ans=min(ans,ty*dp[7]+(ty-tx)*dp[3]);
        }
    }
    else if(x>=0&&y<=0){//右下半区
        ans=min(ans,ty*dp[2]+tx*dp[4]);
        if(tx>ty){
            ans=min(ans,ty*dp[8]+(tx-ty)*dp[4]);
            ans=min(ans,tx*dp[8]+(tx-ty)*dp[1]);
        }
        else {
            ans=min(ans,tx*dp[8]+(ty-tx)*dp[2]);
            ans=min(ans,ty*dp[8]+(ty-tx)*dp[3]);
        }
    }
    else if(x<=0&&y>=0){//左上半区
        ans=min(ans,ty*dp[1]+tx*dp[3]);
        if(tx>ty){
            ans=min(ans,ty*dp[5]+(tx-ty)*dp[3]);
            ans=min(ans,tx*dp[5]+(tx-ty)*dp[2]);
        }
        else {
            ans=min(ans,tx*dp[5]+(ty-tx)*dp[1]);
            ans=min(ans,ty*dp[5]+(ty-tx)*dp[4]);
        }
    }
    else {//左下半区
        ans=min(ans,ty*dp[2]+tx*dp[3]);
        if(tx>ty){
            ans=min(ans,ty*dp[6]+(tx-ty)*dp[3]);
            ans=min(ans,tx*dp[6]+(tx-ty)*dp[1]);
        }
        else {
            ans=min(ans,tx*dp[6]+(ty-tx)*dp[2]);
            ans=min(ans,ty*dp[6]+(ty-tx)*dp[4]);
        }
    }
    cout<<ans<<'\n';
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

E. Swedish Heroes

题意:有一个长度为n的数组a,现在有一个操作,每次选出数组中相邻俩个数a_i,a_{i+1}变成-(a_i+a_{i+1}),再放回原数组,问经过n-1次操作后,剩下一个数字的最大值

知识点:思维,DP

思路:其实最后答案ans=\sum_{i=1}^{n}a[i]*op[i],op[i]\in \{1,-1\},我们设cnt1为op[i]=1的数量,cnt2为op[i]=-1的数量,然后这题有个结论cnt1+2\times cnt2 \equiv 1(mod \ 3),也就是n+cnt2 \equiv 1(mod \ 3)(菜鸡看不出来orz),

然后就是有一个特例不符合条件,就比如 op数组是 1 -1 1 -1 1,也满足刚刚那个结论,但是他不可能出现,因为在开始的第一次操作中一定会让某两个相邻的op是相同的,剩下操作也不会让开始这俩变成不同,所以,除了要满足上述结论,还要满足至少存在某两个相邻的op是相同的

知道这个结论后就可以DP了,设DP[i][0/1/2][0/1]表示前i个数,对3取模后cnt2个数为0/1/2,是否满足条件二的最大答案

初始条件 dp[1][0][0]=a[1],dp[1][1][1]=-a[1]

转移 我们奇偶分开,

i奇数,dp[i][j][0]=dp[i-1][j][0]+a[i];//+一下

dp[i][j][1]=dp[i-1][(j+2)%3][0]-a[i];//上一位是-,这里也-就满足了条件二

i偶数,dp[i][j][0]=dp[i-1][(j+2)%3][0]-a[i];//-一下

dp[i][j][1]=dp[i-1][j][0]+a[i];//上一位是+,这里也+就满足了条件二

然后,dp[i][j][1]=max dp[i][j][1],dp[i-1][j][1]+a[i],dp[i-1][(j+2)%3][1]-a[i]//符合条件二的转移

最后答案就是符合n+i \equiv 1(mod\ 3)的dp[n][i][1],注意n=1的特判和dp的初始化

ops: 

dp[i][0/1/2][0]相当于求了一个op=1 -1 1 -1 1这样的答案

dp[i][0/1/2][1]相当于在上面的基础上变化其中几个op的符合的最大值

初始一定是1 -1 1 -1... 而不是-1 1 -1 1....因为-1开头一定不可能构成条件一的

代码

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const ll N = 2e5+5;
const ll mod =1e9+7;
ll n,a[N];
ll dp[N][3][2];
void solve(){
    cin>>n;
    for(int i=1;i<=n;++i){
        cin>>a[i];
        for(int j=0;j<3;++j){
            dp[i][j][0]=dp[i][j][1]=-1e18;//初始化
        }
    }
    if(n==1){//特判
        cout<<a[1]<<'\n';
        return ;
    }
    dp[1][0][0]=a[1];
    dp[1][1][1]=-a[1];
    for(int i=2;i<=n;++i){
        for(int j=0;j<3;++j){
            if(i&1){//奇数转移
                dp[i][j][0]=dp[i-1][j][0]+a[i];
                dp[i][j][1]=dp[i-1][(j+2)%3][0]-a[i];
            }
            else {//偶数转移
                dp[i][j][0]=dp[i-1][(j+2)%3][0]-a[i];
                dp[i][j][1]=dp[i-1][j][0]+a[i];
            }
            //符合条件二的转移
            dp[i][j][1]=max({dp[i][j][1],dp[i-1][j][1]+a[i],dp[i-1][(j+2)%3][1]-a[i]});
        }
    }
    for(int i=0;i<3;++i){
        if((n+i)%3==1){//定位答案位置
            cout<<dp[n][i][1]<<'\n';
            return ;
        }
    }
}
int main() {
    ios::sync_with_stdio(false);cin.tie(nullptr);cout.tie(nullptr);
    int _=1;//cin>>_;
    while(_--){
        solve();
    }
    return 0;
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值