Codeforces Round #738 (Div. 2) A~D2

A

#include <iostream>

#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 110;

int a[N];

int main()
{
    SIS;
    int t,n;
    cin >> t;
    while(t--){
        cin >> n;
        for(int i=1;i<=n;++i){
            cin >> a[i];
        }
        int ans = a[1];
        for(int i=2;i<=n;++i){
            ans&=a[i];
        }
        cout << ans << '\n';
    }
    return 0;
}

B

#include <iostream>
#include <cstring>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 110;

char a[N];

int main()
{
    SIS;
    int t,n;
    cin >> t;
    while(t--){
        memset(a,0,sizeof(a));
        cin >> n;
        cin >> (a+1);
        bool flag = false;
        for(int i=1;i<=n;++i){
            if(a[i] != '?'){
                flag = true;
                break;
            }
        }
        //全是问号
        if(!flag){
            a[1] = 'R';
            for(int i=2;i<=n;++i){
                if(a[i-1] == 'R') a[i] = 'B';
                else a[i] = 'R';
            }
        }else{
            for(int i=1;i<=n;++i){
                if(a[i] == '?') continue;
                int l = i-1,r=i+1;
                while(a[l] == '?' && l>=1){
                    if(a[l+1] == 'R') a[l] = 'B';
                    else a[l] = 'R';
                    --l;
                }
                while(a[r] == '?' && r<=n){
                    if(a[r-1] == 'R') a[r] = 'B';
                    else a[r] = 'R';
                    ++r;
                }
            }
        }
        cout << (a+1) << '\n';
    }
    return 0;
}

C

感觉C比D1难

#include <iostream>
#include <cstring>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 1e4+10;

int a[N],t,n;

void solve(){
    if(a[1] == 1){
        //n+1 -> 1
        //n+1->1->2->3->...->n
        cout << n+1 << ' ';
        for(int i=1;i<=n;++i) cout << i << ' ';
        cout <<'\n';
        return;
    }
    if(a[n] == 0){
        //n -> n+1
        for(int i=1;i<=n+1;++i) cout << i <<' ';
        cout << '\n';
        return;
    }
    //都不是
    for(int i=n-1;i>=1;--i){
        if(a[i] == 0){
            //i -> n+1
            //1->2->..->i->n+1->i+1->..->n
            for(int j=1;j<=i;++j){
                cout << j << ' ';
            }
            cout << n+1 << ' ';
            for(int j=i+1;j<=n;++j) cout << j << ' ';
            cout << '\n';
            return;
        }
    }
}

int main()
{
    SIS;
    cin >> t;
    while(t--){
        cin >> n;
        for(int i=1;i<=n;++i) cin >> a[i];
        solve();
    }
    return 0;
}

D1

暴力枚举点,然后并查集就行了

#include <iostream>
#include <cstring>
#include <vector>
#include <utility>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 1e3+10;

int f[N],g[N],t,n,m1,m2;

int ffind(int x){
    if(x == f[x]) return x;
    return f[x] = ffind(f[x]);
}
int gfind(int x){
    if(x == g[x]) return x;
    return g[x] = gfind(g[x]);
}

void solve(){

}
vector<pair<int,int>> ve;
int main()
{
    SIS;
    int ansc = 0;
    cin >> n >> m1 >> m2;
    for(int i=1;i<=n;++i) f[i] = g[i] = i;
    for(int i=1,u,v;i<=m1;++i){
        cin >> u >> v;
        f[ffind(u)] = ffind(v);
    }
    for(int i=1,u,v;i<=m2;++i){
        cin >> u >> v;
        g[gfind(u)] = gfind(v);
    }
    for(int i=1;i<n;++i){
        for(int j=i+1;j<=n;++j){
            //是否能添加<i,j>
            if(ffind(i) != ffind(j) && gfind(i)!=gfind(j)){
                f[ffind(i)] = ffind(j);
                g[gfind(i)] = gfind(j);
                ++ansc;
                ve.push_back({i,j});
            }
        }
    }
    cout << ansc << '\n';
    for(auto x : ve){
        cout << x.first << ' ' << x.second << '\n';
    }
    return 0;
}

D2

官方题解好像是启发式合并

但是看不懂,然后到处翻博客;

以下为参考资料

大佬1的题解

大佬2的代码


跟D1的差距就是n变大了

因为D1是暴力枚举,其实有很多边是不需要去枚举的;

为了降低复杂度,我们希望只去连有用的边;

我们假设一号节点为主树,对于左右两森林都如此;

首先,如果对于点 i i i来说,既可以和左森林主树相连,又可以与右森林的主树相连,那么我们直接连接;

对于有些点我们是没有连的,但是是可以连的;

  • 和左主树连会形成环,但是可以和右主树连
  • 和右主树连会形成环,但是可以和左主树连

那么我们将这些点记录下来,让他们两两相连,同时判断不会形成环即可;


举例
在这里插入图片描述
<1,2>在左树连了,但是右树没连;
<1,4>在右树连了,但是左树没连;

那么就连<2,4>是可以连的;

Code

#include <iostream>
#include <cstring>
#include <vector>
#include <utility>
#include <queue>
#define SIS std::ios::sync_with_stdio(false),cin.tie(0),cout.tie(0)

using namespace std;

const int N = 1e5+10;

int f[N],g[N],t,n,m1,m2;

int ffind(int x){
    if(x == f[x]) return x;
    return f[x] = ffind(f[x]);
}
int gfind(int x){
    if(x == g[x]) return x;
    return g[x] = gfind(g[x]);
}
void fmerge(int x,int y){
    int fx = ffind(x);
    int fy = ffind(y);
    f[fx] = fy;
}
void gmerge(int x,int y){
    int gx = gfind(x);
    int gy = gfind(y);
    g[gx] = gy;
}
vector<pair<int,int>> ansv;
queue<int> q1,q2;
int main()
{
    SIS;
    cin >> n >> m1 >> m2;
    for(int i=1;i<=n;++i) f[i] = g[i] = i;
    for(int i=1,u,v;i<=m1;++i){
        cin >> u >> v;
        fmerge(u,v);
    }
    for(int i=1,u,v;i<=m2;++i){
        cin >> u >> v;
        gmerge(u,v);
    }
    for(int i=2;i<=n;++i){
        if(ffind(1)!=ffind(i)&&gfind(1)!=gfind(i)){
            //直接插入ans
            //emplace_back相比push_back 会自动初始化 效率高
            ansv.emplace_back(1,i);
            fmerge(1,i);
            gmerge(1,i);
            continue;
        }
        //能到这 要么和左树的1在一起,要么和右树的1在一起
        if(ffind(1)!=ffind(i)){
            q1.push(i);
        }
        if(gfind(1)!=gfind(i)){
            q2.push(i);
        }
    }
    while(1){
        while(!q1.empty()&&ffind(1)==ffind(q1.front())){
            q1.pop();
        }
        while(!q2.empty()&&gfind(1)==gfind(q2.front())){
            q2.pop();
        }
        if(q1.empty()||q2.empty()) break;
        int h1 = q1.front(),h2 = q2.front();
        ansv.emplace_back(h1,h2);
        fmerge(h1,h2);
        gmerge(h1,h2);
    }
    cout << ansv.size() << '\n';
    for(auto x : ansv){
        cout << x.first << ' ' << x.second << '\n';
    }
    return 0;
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值