Codeforces Round #749 (Div. 1 + Div. 2, based on Technocup 2022 Elimination Round 1)简训

导语

日常

涉及的知识点

思维,图,贪心

链接:Codeforces Round #749 (Div. 1 + Div. 2, based on Technocup 2022 Elimination Round 1)

题目

A Windblume Ode

题目大意:给出n个不重复的正整数求出一个集合使得集合内所有数的和为合数,输出所取数的下标

思路:根据题设条件,不会出现和为2的情况,那么如果n个整数的和是偶数,直接全取,如果是奇数,判断是不是质数,是质数减去一个奇数即可

代码

#include <bits/stdc++.h>
using namespace std;
int t,n,a[121];
int main() {
    cin >>t;
    while(t--) {
        cin >>n;
        int sum=0,pos=0;
        for(int i=1; i<=n; i++) {
            cin >>a[i];
            sum+=a[i];
            if(a[i]&1)pos=i;
        }
        bool flag=0;
        for(int i=2; i<=sum/i; i++)
            if(sum%i==0) {
                flag=1;
                break;
            }
        if(flag) {
            cout <<n<<endl;
            for(int i=1; i<=n; i++) {
                cout <<i;
                if(i!=n)cout <<" ";
            }
            cout <<endl;
        } else {
            cout <<n-1<<endl;
            int ans=0;
            for(int i=1; i<=n; i++) {
                if(i==pos)continue;
                cout <<i;
                ans++;
                if(ans!=n-1)cout <<" ";
            }
            cout <<endl;
        }
    }
    return 0;
}

B Omkar and Heavenly Tree

题目大意:给出n个节点,要求构造出一棵树满足的要求如下:给出m个要求,每个要求给出三个编号a,b,c,要求a和c之间的路径上不能出现b,最后输出树的边

思路:因为题设条件里m小于n,所以必定至少存在一个点不在b中出现,可以找出一个这样的点,然后其他的点都连该点即可

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
int t,n,m,cnt;
bool vis[maxn];
int main() {
    cin >>t;
    while(t--) {
        cin >>n>>m;
        memset(vis,0,sizeof(vis));
        cnt=0;
        int star=0;
        while(m--) {
            int a,b,c;
            cin >>a>>b>>c;
            vis[b]=1;//标记已经被选过
        }
        for(int i=1; i<=n; i++)//找一个未标记点
            if(!vis[i]) {
                star=i;
                break;
            }
        for(int i=1; i<=n; i++)//所有点都连接它
            if(i!=star)cout <<star <<" "<<i<<endl;
    }
    return 0;
}

C Omkar and Determination

题目大意:给出一个迷宫,有些格子是空的,有些被堵上了,如果一个格子为空并且从自己向左或向上走能走出迷宫(即到达一个空白的边界格子),那么认为该格子是“可退出的”,对于一个迷宫,如果给出它哪些格子是“可退出的”,就能重新构造出整个迷宫哪些堵上哪些空白,那么认为这个迷宫是“可决定的”,给出一个n×m的迷宫,有q个询问,每个询问有 x 1 , x 2 x_1,x_2 x1,x2两个整数,代表取原迷宫的 x 1 , x 1 + 1 , … , x 2 − 1 , x 2 x_1,x_1+1,\dots,x_2-1,x2 x1,x1+1,,x21,x2 列作为子迷宫,判断取出来的子迷宫是否是“可决定的”

思路:如果是同一列,一定是可决定的,对于一个格子,如果其上和左都被封,那么即使给出可退出的点,也无法确定这个点是否为空白,那么如果一列存在这样的一个点,这一列与其左边一列就不能选,其余见代码

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int q,n,m,cnt,un[maxn];
bool vis[maxn],unreach[maxn];
char ch;
int main() {
    ios::sync_with_stdio(false);
    cin.tie(0);
    cin >>n>>m;
    for(int i=1; i<=n; i++)
        for(int j=1; j<=m; j++) {
            cin >>ch;
            if(ch=='X')vis[m*(i-1)+j]=1;//标记这里被填
        }
    for(int i=2; i<=n; i++)
        for(int j=2; j<=m; j++)
            if(vis[m*(i-1)+j-1]&&vis[m*(i-2)+j])unreach[j]=1;//找左和上都不能走的点
    for(int i=1; i<=m; i++)if(unreach[i])un[cnt++]=i;//记录列方便二分
    cin >>q;
    while(q--) {
        int u,v;
        cin >>u>>v;
        if(u==v||n==1||m==1||!cnt) {
            cout <<"YES"<<endl;
            continue;
        }
        int pos=upper_bound(un,un+cnt,u)-un;//这里必须用二分来找,遍历会超时,题解骗我
        if(un[pos]>u&&un[pos]<=v)cout <<"NO"<<endl;
        else cout <<"YES"<<endl;
    }
    return 0;
}

D Omkar and the Meaning of Life

题目大意:

思路:以最后一位为基准,先进行n-1次循环,每次循环使1–n-1个数加上i+1,最后一个数加上1,这样每次循环必然可以找到一个数x使得x+i+1=a[n]+1,也就是获得了一个位置与最后一位的正差值,之后再进行n-1次循环使1–n-1个数加上1,最后一个数加上i+1,这样每次循环必然可以找到一个数x使得x+1=a[n]+i+1,也就是获得了一个位置与最后一位的负差值,那么最后直接将全部差值加1直到全部正数即可

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int k,a[121],n,maxx;
int main() {
    cin >>n;
    for(int i=1; i<n; i++) {
        cout <<'?';
        for(int j=1; j<n; j++)
            cout <<' '<<i+1;
        cout <<' '<<1<<endl;
        cin >>k;
        a[k]=-i;
        if(k>0)maxx=max(i,maxx);//如果不是第一个
    }
    for(int i=1; i<n; i++) {
        cout <<'?';
        for(int j=1; j<n; j++)
            cout <<' '<<1;
        cout <<' '<<i+1<<endl;
        cin >>k;
        a[k]=i;
    }
    for(int i=1; i<=n; i++)a[i]+=maxx+1;
    cout <<'!';
    for(int i=1; i<=n; i++)cout <<' '<<a[i];
    return 0;
}

E Moment of Bloom

题目大意:给出n点m边的无向连通图,给定u,v,每次对u到v的简单路径边权+1,判断q次之后是否能使边权全偶

思路:如果在q次中,某点出现了奇数次,代表其必有一条边的权值为奇数,只需要把奇数点对半相连即可,因为一条边连两个奇数点,所以必定产生偶数个奇数点,当所有点次数为偶数时,题目有解,贪心的想,可以直接构造出一棵树来满足题设条件,输出路径的时候用找lca即可

代码

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int n,m,q,ans,head[maxn],cnt,dep[maxn],f[maxn],x[maxn],y[maxn];
bool type[maxn];
struct node {
    int next,to;
} e[maxn];
void Add(int from,int to) {
    e[++cnt].next=head[from];
    head[from]=cnt;
    e[cnt].to=to;
}
void dfs(int u,int fa) {//预处理深度和父节点
    f[u]=fa;
    dep[u]=dep[fa]+1;
    for(int i=head[u]; i; i=e[i].next) {
        int v=e[i].to;
        if(v==f[u]||dep[v])continue;
        dfs(v,u);
    }
}
int main() {
    cin >>n>>m;
    while(m--) {
        int u,v;
        cin >>u>>v;
        Add(u,v);
        Add(v,u);//构造生成树
    }
    cin >>q;
    dep[1]=1;
    dfs(1,0);
    for(int i=1; i<=q; i++) {//判断是否有奇数
        cin >>x[i]>>y[i];
        type[x[i]]^=1;
        type[y[i]]^=1;
    }
    for(int i=1; i<=n; i++)if(type[i])ans++;//统计奇数点
    if(ans)cout <<"NO\n"<<ans/2;
    else {
        cout <<"YES\n";
        for(int i=1; i<=q; i++) {
            int u=x[i],v=y[i];
            vector<int>l,r;
            while(u!=v)//找lca并且存路径
                if(dep[u]>=dep[v])l.push_back(u),u=f[u];
                else r.push_back(v),v=f[v];
            l.push_back(u);
            reverse(r.begin(),r.end());
            cout <<l.size()+r.size()<<endl;
            for(auto p=l.begin(); p!=l.end(); p++)
                cout <<*p<<" ";
            for(auto p=r.begin(); p!=r.end(); p++)
                cout <<*p<<" ";
            cout <<endl;
        }
    }
    return 0;
}

参考文献

  1. CF1586C. Omkar and Determination
  2. CF1586D. Omkar and the Meaning of Life
  3. CF E. Moment of Bloom Codeforces Round #749
  4. Codeforces Round #749 (Div. 1 + Div. 2) 题解
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值