Codeforces Round #742 (Div. 2) A~F

本文介绍了三道编程竞赛题目及其解决方案,涉及 Domino Disaster、MEX 操作和错误加法问题。针对每道题目,分别给出了 AC 代码,并分析了主要思路。对于 Domino Disaster,题目要求根据第一列字母推导出第二列;在 MEX 操作中,通过预处理异或前缀和解决最短序列长度;错误加法问题中,根据奇偶性拆分数字以找到符合条件的数对组合。

A Domino Disaster

题目大意

有n个1*2的多米诺骨牌,给定第一列的字母,求第二列对应的字母

主要思路

遍历所有字母,当前字母为U输出D,当前字母为D输出U,为L输出L,为R输出R

AC代码
#include <bits/stdc++.h>

#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 200010;
int n, a[N], s[N];

signed main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int T;
    cin >> T;
    while(T--)
    {
        cin >> n;
        string s;
        cin >> s;
        for(int i = 0; i < s.size(); i++)
        {
            if(s[i] == 'L') cout << 'L';
            else if(s[i] == 'R') cout << 'R';
            else if(s[i] == 'U') cout << 'D';
            else cout << 'U';
        }
        cout << endl;
    }
    return 0;
}

B MEXor Mixup

题目大意

给定一个序列的MEX的值a和序列的所有数xor的结果b,求这个序列的最短长度

主要思路

由于数组中未出现的最小整数为a, 所以当前序列一定有1~a-1这几个数,我们设这些数xor的结果为c

  • 假设c^a == b,说明想要通过异或一个值得到b那么这个值一定为a,但是数列中不存在a,所以还需要异或两个数才能得到b
  • 假设c^a != b,则我们让c(cb)这个数即可得到b

所以只需预处理一个异或的前缀和即可

AC代码
#include <bits/stdc++.h>

#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 300010;
int n, a[N], s[N];

signed main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    for(int i = 1; i <= 300000; i++) s[i] = s[i - 1] ^ i;
    
    int T;
    cin >> T;
    while(T--)
    {
        int a, b;
        cin >> a >> b;
        if(s[a - 1] == b) cout << a << endl;
        else if((s[a - 1] ^ a) == b) cout << a + 2 << endl;
        else cout << a + 1 << endl;
    }
    return 0;
}

C Carrying Conundrum

题目大意

给定一个错误的加法的定义,然后给定一个数n,求在错误的加法情况下有多少种数对和为n

主要思路

首先由于奇数位的进位只会影响奇数位,所以奇偶分开来看,把n根据奇偶拆成a和b,那么先考虑a有多少种组合方式,(0, a), (1, a - 1), …, (a - 1, 1), (a, 0),有a+1种,b也同理,所以有(a + 1) * (b + 1)种组合方式。

接下来考虑特殊情况,(0, a) (a, 0) (0, b) (b, 0)在组合过程中会有重复

可以组合成 0 0 a b, 0 b 0 a, a 0 0 b, a b 0 0

例如 0 0 a b和 a 0 0 b和a b 0 0重复所以答案-2

AC代码
#include <bits/stdc++.h>

#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 300010;
int n, a[N], s[N];

signed main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int T;
    cin >> T;
    while(T--)
    {
        int n;
        cin >> n;
        int a = 0, b = 0;
        string s =   to_string(n);
        for(int i = 0; i < s.size(); i++)
        {
            if(i % 2) a = a * 10 + s[i] - '0';
            else b = b * 10 + s[i] - '0';
        }
        cout << (a + 1) * (b + 1) - 2 << endl;
    }
    return 0;
}

D Expression Evaluation Error

题目大意

给定一个数n和k,求在10进制下的k个数的和为n的同时,在11进制表示下k个数的和最大,求k个数

主要思路

核心思想:高位尽量不变,如果需要拆分则拆分低位

例如326 5这样的数我们先保证高位不变也就是100 100 100 26,然后继续处理低位,100 100 100 10 16

再举个例子100 2,考虑高位不动拆分低位,则10 90是优于99 1的,具体看代码注释

AC代码
#include <bits/stdc++.h>
 
using namespace std;
 
const int MAX = 200007;
const int MOD = 1000000007;
 
long long split(long long n) {//拆分n这个数
	long long pow10 = 1;
	while (pow10 <= n) {
		if (pow10 == n) {return pow10 / 10;}
		pow10 *= 10;
	}
	return pow10 / 10;
}
 
bool isPow10(long long n) {//判断是不是1000, 1, 100这样的数
	long long pow10 = 1;
	while (pow10 <= n) {
		if (pow10 == n) {return true;}
		pow10 *= 10;
	}
	return false;
}
 
void solve() {
	long long s;
	int n;
	cin >> s >> n;
	vector<long long> curr;//储存当前拆分的数
	curr.push_back(s);
	for (int mv = 0; mv < n - 1; mv++) {
		long long x = -1;
		for (int i = 0; i < curr.size(); i++) {
			if (!isPow10(curr[i])) {//如果有不是100这样的数那么尽量拆分这样的数保证高位
				x = curr[i]; 
				curr.erase(curr.begin() + i); 
				break;
			}
		}
		if (x == -1) {//如果都是100这样的数那么拆分一个最小的保证高位不动
			long long mn = 1000000000007ll;
			for (int i = 0; i < curr.size(); i++) {
				if (curr[i] != 1) {
					mn = min(mn, curr[i]);
				}
			}
			for (int i = 0; i < curr.size(); i++) {
				if (curr[i] == mn) {
					x = curr[i]; 
					curr.erase(curr.begin() + i); 
					break;
				}
			}
		}
		curr.push_back(split(x));
		curr.push_back(x - split(x));
	}
	for (auto i : curr) {
		cout << i << ' ';
	}
	cout << endl;
}
 
int main() {
    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    int tt; cin >> tt; for (int i = 1; i <= tt; i++) {solve();}
    // solve();
}

群友D的骚做法,群友nb!

#include <bits/stdc++.h>

#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 300010;
int n, m, a[N], s[N];

signed main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    int T;
    cin >> T;
    while(T--)
    {
        int j;
        cin >> n >> m;
        for(int i = 1; i < m; i++)
        {
            j = pow(10, (int)log10(n - m + i));
            n -= j;
            cout << j << ' ';
        }
        cout << n << endl;
    }
    return 0;
}

E Non-Decreasing Dilemma

题目大意

给定一个序列有两个操作,给m次询问,每次询问给3个数t,x,y

  • t == 1表示a[x] = y
  • t == 2表示询问x~y区间的非递减子序列的数量
主要思路

有聚聚用树状数组+set过的也是一种思路

这里说一下线段树做法,对每一个节点开一个node包含以下

  • sum表示该节点的答案
  • l,r表示最左边和最右边的值
  • L,R表示最左边和最右边的非递减子序列的最大长度
  • s表示当前序列长度

代码根据节点信息维护即可

AC代码
#include <bits/stdc++.h>

#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second

typedef long long ll;
const int N = 200010;
struct node
{
    int sum, l, r, L, R, s;
}tr[N * 4];
int n, m, a[N];

void pushup(node &root, node l, node r)
{
    root.sum = l.sum + r.sum;
    root.s = l.s+ r.s;
    root.l = l.l, root.r = r.r;
    root.L = l.L, root.R = r.R;
    if(l.r <= r.l)
    {
        root.sum += l.R * r.L;
        if(l.L == l.s) root.L += r.L;
        if(r.R == r.s) root.R += l.R;
    }
}

void build(int u, int l, int r)
{
    if(l == r)
    {
        tr[u] = {1, a[l], a[r], 1, 1, 1};
        return ;
    }
    int mid = l + r >> 1;
    build(u << 1, l, mid), build(u << 1 | 1, mid + 1, r);
    pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
}

node query(int u, int l, int r, int ql, int qr)
{
    if(ql <= l && qr >= r) return tr[u];
    else
    {
        int mid = l + r >> 1;
        
        if(qr <= mid) return query(u << 1, l, mid, ql, qr);
        else if(ql > mid) return query(u << 1 | 1, mid + 1, r, ql, qr);
        else
        {
            node ansl = query(u << 1, l, mid, ql, qr);
            node ansr = query(u << 1 | 1, mid + 1, r, ql, qr);
            node ans;
            pushup(ans, ansl, ansr);
            return ans;
        }
    }
}

void modify(int u, int l, int r, int pos, int v)
{
    if(l == r) tr[u] = {1, v, v, 1, 1, 1};
    else
    {
        int mid = l + r >> 1;
        if(pos <= mid) modify(u << 1, l, mid, pos, v);
        else modify(u << 1 | 1, mid + 1, r, pos, v);
        pushup(tr[u], tr[u << 1], tr[u << 1 | 1]);
    }
}

signed main(void)
{
    ios::sync_with_stdio(false);
    cin.tie(0);
    
    cin >> n >> m;
    for(int i = 1; i <= n; i++) cin >> a[i];
    build(1, 1, n);
    
    while(m--)
    {
        int t, x, y;
        cin >> t >> x >> y;
        if(t == 1) modify(1, 1, n, x, y);
        else
        {
            cout << query(1, 1, n, x, y).sum << endl;
        }
    }
    return 0;
}

F One-Four Overload

题目大意

给定一个n*m的二维字符数组使得X上的值等于周围’.'位置上的和且为5的倍数,求一种构造方法

主要思路

优先构造‘.’上的数,因为只能有1和4所以我们就使用这两个数

  • 首先考虑不合法的情况,当X周围.的个数为奇数时一定不合法,因为无法凑出5的倍数
  • 其次考虑性质,想要X位置上的数是5的倍数,要么周围是1和4,要么是两个1两个4,如果是两个1两个4的情况,一定要让1的对面是1,4的对面是4才不会影响后面的构造
  • 基于性质所以我们先让奇数列的’.'为1,偶数列的为4,接下来考虑不合法情况,当 当前位置为X时,且当前位置的上一行的位置也为X,说明当前位置的右边一定与左边的数不同,也就是奇偶顺序改变

例如 X

​ 1 X 4

  • 然后再根据X周围的’.'判断X的值即可
AC代码
#include <bits/stdc++.h>

#define int long long
using namespace std;
#define debug(a) cout << #a << " = " << a << endl;
#define x first
#define y second
typedef long long ll;
const int N = 510;

int n, m;
char mp[N][N];
int ans[N][N];
int dx[] = {0, 1, 0, -1}, dy[] = {1, 0, -1, 0};

signed main(void)
{
    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 >> mp[i][j];
        }
    }
    
    int p = 0;
    for(int i = 1; i <= n; i++)
    {
        p = 0;
        for(int j = 1; j <= m; j++)
        {
            if(mp[i][j] == 'X')
            {
                p ^= (mp[i - 1][j] == 'X');
                int cnt = 0;
                for(int k = 0; k < 4; k++)
                {
                    if(mp[i + dx[k]][j + dy[k]] == '.') cnt++;
                }
                if(cnt & 1)
                {
                    cout << "NO" << endl;
                    return 0;
                }
            }
            else
            {
                ans[i][j] = ((j + p) & 1) ? 1 : 4;
            }
        }
    }
    
    cout << "YES" << endl;
    for(int i = 1; i <= n; i++)
    {
        for(int j = 1; j <= m; j++)
        {
            if(mp[i][j] == 'X')
            {
                int s = 0;
                for(int k = 0; k < 4; k++)
                {
                    if(mp[i + dx[k]][j + dy[k]] == '.') s += ans[i + dx[k]][j + dy[k]];
                }
                cout << s << ' ';
            }
            else cout << ans[i][j] << ' ';
        }
        cout << endl;
    }
    return 0;
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值