Codeforces Round #741 (Div. 2)

有一说一这套题感觉有点差劲 打完之后感觉没有什么提高 全是结论题

A. The Miracle and the Sleeper

题意: 给出一个l, r区间从中要选择a和b 使得 a mod b 最大
贪心 很容易的可以想到当l, r区间足够大的时候 b = a / 2 + 1 (a = r)的时候 a mod b取最大值 当l > r / 2 + 1的时候就b无法取到r / 2 + 1了 则直接r mod l 即可
代码

#include 
 
#define int long long
 
using namespace std;
 
void solve()
{
	int l, r;
	cin >> l >> r;
	if (l == r) cout << 0 << endl;
	else if (l <= r / 2)
	{
		int x = r / 2;
		if (r == x * 2) cout << x - 1 << endl;
		else cout << x << endl;
	}
	else cout << r - l << endl;
}
 
signed main()
{
	int t;
	cin >> t;
	
	while (t -- )
	{
		solve();
	}
	
	return 0;
}

B. Scenes From a Memory

给出一串很长的只包含数字的字符串 让我们从中删除最多的数使得删完之后的数为合数 剩下最后一位的时候 1也符合题意 贪心的想 总之最后肯定就只剩下一位数或者两位数 我们可以先用素数筛把所有100之内的素数筛掉 然后判断一位的是否有符合题意的 没有的话再判断两位的
代码

#include 
 
#define int long long
 
using namespace std;
 
const int N = 110;
 
int prime[N], cnt;
bool st[N];
 
void init()
{
	st[1] = true;
	for (int i = 2; i <= N; i ++ )
	{
		if (!st[i]) prime[cnt ++ ] = i;
		for (int j = 0; prime[j] * i <= N; j ++ )
		{
			st[prime[j] * i] = true;
			if (i % prime[j] == 0) break;
		}
	}
}
 
void solve()
{
	int n, ans;
	string s;
	cin >> n >> s;
	
	int flag = 0;
	for (int i = 0; i < n; i ++ )
		if (st[s[i] - '0']) 
		{
			ans = s[i] - '0';
			flag = 1;
			break;
		}
	
	if (!flag) 
		for (int i = 0; i < n; i ++ )
			for (int j = i + 1; j < n; j ++ )
			{
				int a = s[i] - '0', b = s[j] - '0';
				if (st[a * 10 + b])
				{
					ans = a * 10 + b;
					flag = 2;
					break;
				}
			}
	cout << flag << endl << ans << endl;
}
 
signed main()
{
	int t;
	cin >> t;
	
	init();
	
	while (t -- )
	{
		solve();
	}
	
	return 0;
}

C. Rings

给出一串01序列 然后从中筛选出两个子序列 s1 s2 (两序列的长度不能小于原序列总长度 / 2)使得这两个序列由二进制转化为十进制可以满足 f[s1] = f[s2] * k 其中k为正整数
我们可以假设k=1 即变为求 f[s1] = f[s2]怎么取了
那么对于一个不含0的01序列 例如11111 我们可以从第一位取到第三位 再从第三位取到第五位 即只要两序列1个数相同则满足题意
若对于一个含0的呢? 比如100111101 我们可以遍历一遍 找0的位置 如果0在n/2前面 则我们往后取 一直取到最后即原序列的长度n处 使得s1包含这个0 s2不包含这个0 但从0往后的所有位都相等 很明显 f[s1]=f[s2]
如果0在后面呢 我们依然可以这样取 不过要变为反方向 因为某个数乘以2 即左移一位 后面加0 依然可以f[s1] = f[s2]
** 代码**

#include 
 
#define int long long
 
using namespace std;
 
const int N = 110;
 
int prime[N], cnt;
bool st[N];
 
void init()
{
	st[1] = true;
	for (int i = 2; i <= N; i ++ )
	{
		if (!st[i]) prime[cnt ++ ] = i;
		for (int j = 0; prime[j] * i <= N; j ++ )
		{
			st[prime[j] * i] = true;
			if (i % prime[j] == 0) break;
		}
	}
}
 
void solve()
{
	int n, m;
	string s;
	cin >> n >> s;
	m = n / 2;
	bool flag = 0;
	for (int i = 0; i < n; i ++ )
		if (s[i] == '0')
		{
			flag = true;
			if (i < m) 
				cout << i + 1 << ' ' << n << ' ' << i + 2 << ' ' << n << endl;	
			else
				cout << 1 << ' ' << i + 1 << ' ' << 1 << ' ' << i << endl;
			break;
		}
	if (!flag) cout << 1 << ' ' << m << ' ' << m + 1 << ' ' << m * 2 << endl;
	
}
 
signed main()
{
	int t;
	cin >> t;
	
	while (t -- )
	{
		solve();
	}
	
	return 0;
}


D1. Two Hundred Twenty One (easy version)

题意 : 给出一串序列 只包含 ‘+’ ‘-’ 然后某序列的值为先加后减加减相错 即a1 - a2 + a3 - a4 + a5 … 给出m组询问 问某l, r区间内最少删除几个可以使得此区间和为0
我们考虑这样一个问题 如果我们删掉中间的某个字符会带来什么影响
删掉中间某个字符会使后面所有字符的值变为相反数 那么我们用前缀和来维护从1到n的总和
分类讨论 :
当区间个数为奇数个的时候 假如值为1或-1 我们可以删掉最后一个或者第一个 当值的绝对值>1的时候我们可以删掉中间的一个数 使得后面自区间的值变为前面自区间值的相反数
当区间个数为偶数时也同理 假如值为0 我们可以不删 假如不为0 删掉最后一个将其变为奇数个数 再从中间删掉一个
所以说答案肯定为 0 1 2 分类讨论即可
代码

#include 
 
#define int long long
 
using namespace std;
 
const int N = 3e5 + 10;
 
int sum[N];
 
void solve()
{
	memset(sum, 0, sizeof sum);
	
	int n, m;
	char s[N];
	cin >> n >> m >> s + 1;
	for (int i = 1; i <= n; i ++ )
	{
		int x;
		if (s[i] == '+')
		{
			if (i & 1) sum[i] = sum[i - 1] + 1;
			else  sum[i] = sum[i - 1] - 1;
		}
		else
		{
			if (i & 1) sum[i] = sum[i - 1] - 1;
			else  sum[i] = sum[i - 1] + 1;
		}
	}
	while (m -- )
	{
		int l, r;
		cin >> l >> r;
		if ((r - l + 1) & 1) puts("1");
		else
		{
			int cnt = sum[r] - sum[l - 1];
			if (cnt) puts("2");
			else puts("0");
		}
	}
}
 
signed main()
{
	ios::sync_with_stdio(false);
	cin.tie(0);
	
	int t;
	cin >> t;
	
	while (t -- )
	{
		solve();
	}
	
	return 0;
}

D2. Two Hundred Twenty One (hard version)

D1的hard版 不止要求需要去掉几个数字了还要求删掉的那些
对于删除思路 当区间为奇时要删掉x处 且sum[x - 1] - sum[l - 1] = sum[r] - sum[x] 当区间为偶时随便删一处(这里我们删掉 l 处的值) 然后按照奇数方法去做
我们已知 sum[x - 1] - sum[l - 1] = sum[r] - sum[x] 可以推导出 sum[r] + sum[l - 1] = sum[x] + sum[x - 1] 故我们想到可以用一个vector存储值为sum[x] + sum[x-1]的所有下标 然后二分去找第一个大于等于当前 l 的即可
代码

#include 

using namespace std;

const int N = 1e6 + 100, INF  = 0x3f3f3f3f;

int n, m;
int sum[N];
map<int, vector<int>> node;

int cal(int l, int r)
{
	int x = sum[r] + sum[l - 1];
	int t = *lower_bound(node[x].begin(), node[x].end(), l);
	return t;
}

void solve()
{
	char s[N];
	node.clear();
	cin >> n >> m >> s + 1;
	
	for (int i = 1; i <= n; i ++ )
	{
		sum[i] = sum[i - 1] + (i & 1 ? 1 : -1) * (s[i] == '+' ? 1 : -1);
		node[sum[i] + sum[i - 1]].push_back(i);
	}
	
	while (m -- )
	{
		int l, r;
		scanf("%d%d", &l, &r);
		if (sum[r] - sum[l - 1] == 0) puts("0");
		else if ((r - l + 1) & 1)
		{
			puts("1");
			printf("%d\n", cal(l, r));
		}
		else
		{
			puts("2");
			printf("%d %d\n", l, cal(l + 1, r));
		}
	}
}

signed main()
{
	int t;
	cin >> t;
	
	while (t -- )
	{
		solve(); 
	} 
	
	return 0;
}

你可能感兴趣的:(Codeforces,算法)