Codeforces Round #741 (Div. 2) (A~D2)题解

比赛链接:点击这里传送

官方题解链接:点击这里传送


1562A The Miracle and the Sleeper 题目链接:点击这里传送

在这里插入图片描述
题意:
给定一个范围 [ l , r ] \left[{l},{r}\right] [l,r],在这个范围内任意选两个数a和b,使得b mod a最大
思路:
如果l>r/2,直接输出r-l。否则输出(r-1)/2。因为理论上模数最大时除数为1,此时模数最大为(r-1)/2.

#include<bits/stdc++.h>
using namespace std;
int t, a, b;

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> a >> b;
		cout << min(b - a, (b - 1) / 2) << endl;
	}

	return 0;
}

1562B Scenes From a Memory 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一个数(不含0),你可以删除这个数中的任何一位。现要求你尽可能多地删除一些数,使得剩余的这些位组成的数不为质数。(1可以,2不行)。输出剩余的位数和这个非质数。
思路:
这是道结论题。
有1,4,6,8,9的只剩下一位就行。
剩余情况就是这个数只包含2,3,5,7。那么只需剩下两位,下面给出证明。
结论一:如果这四个数有任意数重复出现,那么就可以整除11,如22,33.
由于题目已保证不会有非法情况的出现,也就是说没有诸如23,37,73,2,7,3,5的数。
也就是说出现的是二位数的话,肯定不是质数。
结论二:只会出现合法的二位数
讨论三位数的情况:

  • 2,3,5 2和5结尾的不必多说,523和253也都可以转化成2和5结尾的非质数。
  • 2,3,7 27和72都不是质数,无论3在哪都无所谓
  • 2,5,7 还是2和7
  • 3,5,7 57和75都不是质数,无论3在哪都无所谓

结论三:所有只包含2,3,5,7的三位数都可以变成合法的二位数
四位数可以变成任意三位数,而三位数已经全部得证合法。五位数以上直接有数字重复。
综上得证

#include<bits/stdc++.h>
using namespace std;
string s;
int t, n;
int prime[105];
void pre()
{
	for (int i = 1; i <= 100; i++) prime[i] = 1;
	for (int i = 2; i <= 100; i++)
	{
		int flag = 1;
		for (int j = 2; j <= sqrt(i); j++)
		{
			if (i % j == 0)
			{
				flag = 0;
				break;
			}
		}
		if (flag == 1)
		{
			prime[i] = 0;
		}
	}
}
void solve()
{
	for (int i = 0; i < s.length(); i++)
	{
		if (s[i] == '1' || s[i] == '4' || s[i] == '6' || s[i] == '8' || s[i] == '9')
		{
			cout << 1 << endl;
			cout <<s[i] << endl;
			return;
		}
	}
	cout << 2 << endl;
	for (int i = 0; i < s.length()-1; i++)
	{
		for (int j = i + 1; j < s.length(); j++)
		{
			int temp = (s[i] - '0') * 10 + (s[j] - '0');
			if (prime[temp] == 1)
			{
				cout << temp << endl;
				return;
			}
		}
	}
}

int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	pre();
	cin >> t;
	while (t--)
	{
		cin >> n >> s;
		solve();
	}

	return 0;
}

1562C Rings 题目链接:点击这里传送

在这里插入图片描述
题意:
给出一个01串,选取两个不同的子串(下面的值可以重复),他们的长度要大于等于 n 2 \frac{n}{2} 2n。将这两个子串转化为十进制(有前导零去除前导零),使得valuea=valueb × \times ×k,k ∈ \in 非负整数。输出这两个子串的范围
思路:

  • 当字符串为全1串时,输出 [ 1 , n − 1 ] \left[1,n-1\right] [1,n1] [ 2 , n ] \left[2,n\right] [2,n],此时k为1
  • 当字符串的第一个0出现在前一半时,形如1110xxxxxxxx。设这个位置为pos,输出 [ p o s , n ] \left[pos,n\right] [pos,n] [ p o s + 1 , n ] \left[pos+1,n\right] [pos+1,n],此时k为1.
    n-(pos+1)+1≥ n 2 \frac{n}{2} 2n
    pos≤ n 2 \frac{n}{2} 2n
    ∵ \because 实际上字符串的pos从0开始
    ∴ \therefore pos< n 2 \frac{n}{2} 2n
  • 当字符串的第一个0出现在后一半时,形如111111110xxxx。输出 [ 1 , p o s ] \left[1,pos\right] [1,pos] [ 1 , p o s − 1 ] \left[1,pos-1\right] [1,pos1],此时k为2.
#include<bits/stdc++.h>
using namespace std;
string s;
int t, n;
void solve()
{
	int index=-1;//字符串中第一个0出现的位置
	for (int i = 0; i < s.length(); i++)
	{
		if (s[i] == '0')
		{
			index = i;
			break;
		}
	}
	if (index == -1)//字符串全为1
	{
		cout << 1 << " " << n - 1 << " " << 2 << " " << n << endl;
	}
	else if (index < n / 2)//011111 11111 
	{
		cout << index + 1 << " " << n  << " " << index + 2 <<" "<< n << endl;
	}
	else//111110   11111
	{
		cout << 1 << " " << index + 1 << " " << 1 << " " << index << endl;
	}
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n >> s;
		solve();
	}
	return 0;
}

1562D1 Two Hundred Twenty One (easy version) 题目链接:点击这里传送

在这里插入图片描述
题意:
有一串只包含’+‘和’-'的字符串,分别表示+1和-1。选定 [ l , r ] \left[l,r\right] [l,r]范围内的子串,使得这个子串的值变为 a l a_l al,- a l + 1 a_{l+1} al+1, a l + 2 a_{l+2} al+2,……, ( − 1 ) i − l {(-1)}^{i-l} (1)il × \times × a i a_i ai。把这些值加起来得到一个数为C。要求通过删除序列中的一些元素(删除后后面的会补位上来)使得C为0.输出删除的数目。
思路:
因为这些值的元素都为1和-1,所以最后的序列长度肯定为偶数。奇数的话最少删一个,偶数的话最少删两个(如果他不为0)。
如果删除一个数,那么后面的元素补上来并且符号全部取反,就相当于原先是a+x+b变成了a-b=0。很明显这个x是存在的,因为元素值都为1且a+b+x ≠ \neq = 0。根据上面结论,如果值直接为0,就输出0,否则奇数输出1,偶数输出2(先任意删除一个数再进行运算)。

#include<bits/stdc++.h>
using namespace std;
#define MAXN 300005
int n, m, t;
string s;
int l, r;
int a[MAXN];
int sum[MAXN];//奇数和奇数,偶数和偶数
int get(int l, int r)
{
	int ans = 0;
	if (l % 2 == 1 && r % 2 == 1) //+-+-+
	{
		int temp = max(0, l - 2);
		ans = sum[r] - sum[temp];
		ans -= sum[r - 1] - sum[l-1];
	}
	else if (l % 2 == 1 && r % 2 == 0) //+-+-
	{
		int temp = max(0, l - 2);
		ans = sum[r - 1] - sum[temp];
		ans -= sum[r] - sum[l-1];
	}
	else if (l % 2 == 0 && r % 2 == 1)//-+-+
	{
		ans = sum[r] - sum[l-1];
		ans -= sum[r - 1] - sum[l - 2];
	}
	else//-+-+-
	{
		ans = sum[r - 1] - sum[l - 1];
		ans -= sum[r] - sum[l - 2];
	}
	return ans;
}
int main()
{
	ios::sync_with_stdio(false); cin.tie(0); cout.tie(0);
	cin >> t;
	while (t--)
	{
		cin >> n >> m;
		cin >> s;
		for (int i = 0; i < s.length(); i++)
		{
			if (s[i] == '+') a[i + 1] = 1;
			else a[i + 1] = -1;
		}
		sum[1] = a[1];
		for (int i = 2; i <= n; i++)
		{
			sum[i] = sum[i - 2] + a[i];
		}
		while (m--)
		{
			cin >> l >> r;
			int ans = 0;
			//[l,pos-1]=x/2 pos [pos+1,r]=x/2
			ans = get(l, r);
			if (ans == 0) cout << 0 << endl;
			else if ((r - l + 1) % 2 == 1) cout << 1 << endl;
			else if ((r - l + 1) % 2 == 0) cout << 2 << endl;
		}
	}

	return 0;
}

1562D2 Two Hundred Twenty One (hard version) 题目链接:点击这里传送

题意:
与D1的唯一区别就是还要输出删除的元素的位置。
推一下公式,设删除的位置是pos,pos ∈ \in [ l , r ] \left[l,r\right] [l,r]
sum[pos-1]-sum[l-1]=sum[r]-sum[pos]
sum[pos-1]+sum[pos]=sum[r]+sum[l-1]
只要找到一个pos满足上述式子即可。
关于如何证明偶数位取反后前缀和依然成立的证明,官方题解写了看不懂。
关于查找的过程,O(n)的写法是会超时的,官方题解写了二分答案看不懂。
直接给出官方题解的代码吧。

#include <iostream>

using namespace std;

int a[1000000 + 5], p[1000000 + 5];

int get_sum(int l, int r) {
    if (l > r) {
        return 0;
    }
    return (l % 2 == 1) ? p[r] - p[l - 1] : p[l - 1] - p[r];
}

int check_elimination(int l, int r, int m) {
    return ((m - l + 1) % 2 == 1)
    ? get_sum(l, m - 1) + get_sum(m + 1, r)
    : get_sum(l, m - 1) - get_sum(m + 1, r);
}

int get_sign(int m) {
    return m > 0 ? 1 : -1;
}

int calculate_odd_segment(int l, int r) {
    if (l == r) {
        return l;
    }
    int pos = 0;
    int lb = l;
    int rb = r;
    while (lb < rb) {
        int mb = (lb + rb) / 2;
        int lq = check_elimination(l,r,lb);
        int mq = check_elimination(l,r,mb);
        int rq = check_elimination(l,r,rb);
        if (lq == 0) {
            pos = lb;
            break;
        }
        if (mq == 0) {
            pos = mb;
            break;
        }
        if (rq == 0) {
            pos = rb;
            break;
        }
        if (get_sign(lq) == get_sign(mq)) {
            lb = mb;
        } else {
            rb = mb;
        }
    }
    return pos;
}

int main() {
    ios_base::sync_with_stdio(false);
    cin.tie(nullptr);
    int t;
    cin>>t;
    while (t--) {
        int n, q;
        cin >> n >> q;
        string ss;
        cin >> ss;
        for (int i = 1; i <= n; i++) {
            a[i] = (ss[i - 1] == '+' ? 1 : -1);
        }
        p[0] = 0;
        for (int i = 1; i <= n; i++) {
            p[i] = p[i - 1] + (i % 2 == 1 ? a[i] : -a[i]);
        }
        for (int o = 0; o < q; o++) {
            int l, r;
            cin >> l >> r;
            if (get_sum(l, r) == 0) {
                cout << "0\n";
                continue;
            }
            bool even = false;
            if ((r - l + 1) % 2 == 0) {
                even = true;
                l++;
            }
            int pos = calculate_odd_segment(l, r);
            if (even) {
                cout << "2\n" << l - 1 << " "<< pos << '\n';
            } else {
                cout << "1\n" << pos << '\n';
            }
        }
    }
}

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值