Codeforces Round #741 (Div. 2)A~E

A. The Miracle and the Sleeper

题意:在范围为l到r的区间内选择两个数a和b(a>b),求a%b最大

分析:如果a取l,b取r,如果b不到a的两倍,那么直接取边界b-a,否则,一个取余数+1,一个取到2余数+1

代码:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 1e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], g[N], dp[N];

void solve()
{
	int a, b;
	cin >> a >> b;
	if (a * 2 > b) printf("%d\n", b - a);
	else printf("%d\n", (b + 1) / 2 - 1);
}

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

B. Scenes From a Memory

题意:给一个五十位的十进制数,删除最多的数字个数,使得剩下的数是合数,输出这个合数,保证存在合法解

分析:只要胆子大,咋也能过,事实上,最多只会剩下两位数,分析讨论,一位数的情况有1,3,6,8,9若存在直接就是答案,若不存在,则剩下0,2,3,5,7五个数,因为题目说没有前导零的存在,所以0但凡存在,前面绝对会有一个数,使得两位数成立,剩下四个数,排列组合两位数,不合法的情况只有三种23,53,73,而题目直接表示存在合法解,所以包含这三种情况的两位数不存在,三位数必有解,而位数超过两位数的话,则必定成立,所以只要便利两次就好

代码:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 1e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], g[N], dp[N];

string a;

void init()
{
	s[1] = 1;
	for (int i = 2; i < N; i++)
	{
		if (s[i]) continue;
		for (int j = 2 * i; j < N; j += i)
		{
			s[j] = 1;
		}
	}
}

void solve()
{
	cin >> n >> a;
	for (int i = 0; i < n; i++)
		if (s[a[i] - '0'])
		{
			printf("1\n%d\n", a[i] - '0');
			return;
		}
	for (int i = 0; i < n; i++)
		for (int j = i + 1; j < n; j++)
			if (s[(a[i] - '0') * 10 + a[j] - '0'])
			{
				printf("2\n%d\n", (a[i] - '0') * 10 + a[j] - '0');
				return;
			}
}

int main()
{
	init();
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}

C. Rings

题意:给出长度为n的01字符串,选择两组两端不完全相同的子串a,b,使得a换算二进制下是b的非负整数倍,两组子串长度需\(\geq \left \lfloor \frac{n}{2} \right \rfloor\),输出a的左右端点, b的左右端点

分析:换算二进制下,最左端的0没有用,最右端的0相当于*2,所以找寻0的位置,如果找不到的话,意味着全1,输出长度大于\(\left \lfloor \frac{n}{2} \right \rfloor\)的任意长度相同位置不同的四个位置即可

代码:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 1e5 + 10, mod = 1e9 + 7, P = 13331;

int n, m, t, k;

int s[N], g[N], dp[N];

char str[N];

void solve()
{
	cin >> n >> str + 1;
	int i = 1;
	for (; i <= n; i++)
		if (str[i] == '0')
			break;
	if (i == n + 1)
		printf("1 %d 2 %d\n", n - 1, n);
	else if (i <= n / 2)
		printf("%d %d %d %d\n", i, n, i+1, n);
	else printf("%d %d %d %d\n", 1, i, 1, i-1);
}

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

D2. Two Hundred Twenty One (hard version)

题意:对于任意相连的一组数,对奇数为添加,对偶数位减少,给出+-字符串,+表示1,-表示-1,每次询问l,r的区间,问若使得这段值和为0,最少要拿走几个数(拿走后位置奇偶关系改变),拿走的这些数在哪几位,输出

分析:首先可以预处理前缀和,O(1)求出l到r的区间和,区间和为0,答案为0;区间和为奇数,分析,从l到r必定是连续的,每个数都存在,所以一定存在一个数使得删去后(假设位置在x),l到x-1等于x+1到r,所以奇数删一个,偶数先随便删,变成奇数删一个,不为0的偶数是两个,而因为知道l和r所以可以算出要删哪个数,s[x-1]-s[l-1]=s[r]-s[x],x为1或者-1,s[x-1]=(s[l-1]+s[r]+x)/2,一般情况下x正负1不影响,但当l=r的时候,x必须是朝向r的一边,当然这种也可以特判,至此题目转化为在l到r的区间内查找s[x]的位置,这个也可以预处理,对于所有的前缀和,我们存放到vector里,vector[i]存放的是前缀和为i的位置,这样就可以二分查找,时间复杂度O(nlogn),因为前缀和范围为-n到n,所以vector要开2n,每次存放位置+n,把-n到n转化成0到2n,就可以存放了

代码:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 3e5 + 10, mod = 1e9 + 7;

int n, m, t, k;

int s[N], g[N], dp[N];

char str[N];

vector<int> vec[2 * N];

void solve()
{
	cin >> n >> m >> str + 1;
	for (int i = 1; i <= n; i++)
	{
		if (str[i] == '+') s[i] = 1;
		else s[i] = -1;
		if (i % 2 == 0) s[i] = -s[i];
		s[i] += s[i-1];
		vec[s[i] + n].push_back(i);
	}
	while (m--)
	{
		int l, r;
		cin >> l >> r;
		if (s[r] - s[l-1])
		{
			if ((r - l + 1) & 1)
			{
				printf("1\n");
				int a = 0;
				if (s[r] > s[l-1]) a = (s[r] + s[l-1] + 1) / 2;
				else a = (s[r] + s[l-1] - 1) / 2;
				int b = lower_bound(vec[a+n].begin(), vec[a+n].end(), l) - vec[a+n].begin();
				printf("%d\n", vec[a+n][b]);
			}
			else
			{
				printf("2\n");
				printf("%d ", l++);
				int a = 0;
				if (s[r] > s[l-1]) a = (s[r] + s[l-1] + 1) / 2;
				else a = (s[r] + s[l-1] - 1) / 2;
				int b = lower_bound(vec[a+n].begin(), vec[a+n].end(), l) - vec[a+n].begin();
				printf("%d\n", vec[a+n][b]);
			}
		}
		else printf("0\n");
	}
	for (int i = 0; i <= 2 * n; i++) vec[i].clear();
}

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

E. Rescue Niwen!

题意:对于给定字符串按顺序展开(举例:abc展开为a,ab,abc,b,bc,c)所有子串组成的最大严格递增序列中子串数量。

分析:初始有一个结论,如果选择了一个元素,那么对于这个元素的所有情况的后缀子串全都要选,因为选了不可能更坏,然后容易想到后缀数组可以O(nlog)的算出对于第i个开头的后缀子串的大小关系,辅助数组可以O(n)求出不同后缀数组的最长公共子串前缀长度,题目就转成了,有n个数,每个数可以由之前比自己这个数小的转移,转移会增加当前长度-两者的最长公共前缀的价值,求这个价值最大值,可以\(n^{2}\)枚举,但是前面的操作实在打不对,就看了大佬的思路
大佬思路:可以\(n^{2}\)的预处理所有后缀子串的最长公共前缀,只要倒着便利即可,对于所有情况的c[i][j]只有当s[i]=s[j]的时候,它的长度才会改变,并且会变成1+c[i+1][j+1](c[i+1][j+1]表示当前i和j搭配后,后面的i+1和j+1开始的两个后缀子串的最长公共前缀),对于边界的越界情况都为0,因为另一个子串长度为空。预处理之后O(\(n^{2}\))转移,对于比自己小的,直接转移,和自己一样的,比较最长公共前缀之后一位的关系,小就也可以选择进行转移,大则不能。
代码:

#include <cstring>
#include <iostream>
#include <algorithm>
#include <map>
#include <vector>
#include <cmath>

#define x first
#define y second
#define MAX(a,b,c) max(max(a, b),c)
#define MIN(a,b,c) min(min(a, b),c)
#define debug(x) cout<<#x<<":"<<x<<endl;
#define endl '\n'

using namespace std;

typedef long long ll; 
typedef pair<int, int> PII;

const int N = 5e3 + 10, mod = 1e9 + 7;

int n, m, t, k;

int g[N], dp[N], c[N][N], l[N];

string s;

void solve()
{
	cin >> n >> s;
    for (int i = n; i >= 0; --i) 
		for (int j = n; j >= 0; --j)
			c[i][j]=0;
    for (int i = n - 1 ; i >= 0; --i)
	{
        l[i]=0;
        for (int j = n - 1; j >= 0; --j)
			if (s[i] == s[j])
				c[i][j] = c[i + 1][j + 1] + 1;
    }
    int a = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j)
			if (l[j] - c[i][j] > l[i] && s[j + c[i][j]] < s[i + c[i][j]])
				l[i] = l[j] - c[i][j];
        if ((l[i] += n - i) > a)
			a = l[i];
    }
    cout << a << endl;
}

int main()
{
    t = 1;
    cin >> t;
    while(t--) 
        solve();
    return 0;
}
posted @ 2021-08-27 22:21  forleaves  阅读(108)  评论(0)    收藏  举报